class InteractionSystem { constructor(scene) { this.scene = scene; this.iso = new IsometricUtils(48, 24); // Input listener setup (only once) this.scene.input.on('pointerdown', (pointer) => { if (pointer.button === 0) { // Left Click this.handleLeftClick(pointer); } }); // Loot Array this.drops = []; } handleLeftClick(pointer) { if (!this.scene.player) return; // 1. Account for camera and offset const worldX = pointer.worldX - this.scene.terrainOffsetX; const worldY = pointer.worldY - this.scene.terrainOffsetY; // 2. Convert to Grid const gridPos = this.iso.toGrid(worldX, worldY); // 3. Check distance const playerPos = this.scene.player.getPosition(); const dist = Phaser.Math.Distance.Between(playerPos.x, playerPos.y, gridPos.x, gridPos.y); // Allow interaction within radius of 2.5 tiles if (dist > 2.5) { console.log('Too far:', dist.toFixed(1)); return; } console.log(`☝️ Clicked tile: ${gridPos.x},${gridPos.y}`); // DETERMINE TOOL / ACTION let activeTool = null; const uiScene = this.scene.scene.get('UIScene'); const invSys = this.scene.inventorySystem; if (uiScene && invSys) { const selectedIdx = uiScene.selectedSlot; const slotData = invSys.slots[selectedIdx]; if (slotData) activeTool = slotData.type; } // 0. Build Mode Override if (this.scene.buildingSystem && this.scene.buildingSystem.isBuildMode) { this.scene.buildingSystem.tryBuild(gridPos.x, gridPos.y); return; // Consume click } // 3.5 Check for NPC Click if (this.scene.npcs) { for (const npc of this.scene.npcs) { if (Math.abs(npc.gridX - gridPos.x) < 2.5 && Math.abs(npc.gridY - gridPos.y) < 2.5) { console.log(`🗣️ Interact with NPC: ${npc.type}`); if (npc.type === 'zombie') { // Taming Logic npc.toggleState(); return; // Done } if (npc.type === 'merchant') { // Open Trade Menu if (uiScene && invSys) { uiScene.showTradeMenu(invSys); } return; // Stop processing } return; // Stop processing other clicks (farming/terrain) if clicked NPC } } } // 4. Try Farming Action (Tilling, Planting, Harvesting) if (this.scene.farmingSystem) { const didFarm = this.scene.farmingSystem.interact(gridPos.x, gridPos.y, activeTool); if (didFarm) { // Animation? return; } } // 5. Try damage decoration const id = `${gridPos.x},${gridPos.y}`; if (this.scene.terrainSystem.decorationsMap.has(id)) { const decor = this.scene.terrainSystem.decorationsMap.get(id); // Calculate Damage based on Tool let damage = 1; // Default hand damage // Tool Logic if (decor.type === 'tree' && activeTool === 'axe') { damage = 3; // Axe destroys tree fast } else if ((decor.type === 'bush' || decor.type === 'stone') && activeTool === 'pickaxe') { damage = 3; // Pickaxe destroys stone fast } // Apply damage const result = this.scene.terrainSystem.damageDecoration(gridPos.x, gridPos.y, damage); if (result === 'destroyed') { // Play proper sound if (decor.type === 'tree') { if (this.scene.soundManager) this.scene.soundManager.playChop(); } else { // Play stone break sound (using chop for now or generic hit) if (this.scene.soundManager) this.scene.soundManager.playChop(); } // AUTO-LOOT directly to Inventory let lootType = 'wood'; // Default let lootCount = 1; if (decor.type === 'tree') { lootType = 'wood'; lootCount = 3 + Math.floor(Math.random() * 3); // 3-5 wood } else if (decor.type === 'bush' || decor.type === 'stone') { lootType = 'stone'; lootCount = 2 + Math.floor(Math.random() * 3); // 2-4 stone } else if (decor.type === 'flower') { lootType = 'seeds'; // Flowers drop seeds? Or flower item? lootCount = 1; } console.log(`🎁 Auto-looted: ${lootCount}x ${lootType}`); if (invSys) { invSys.addItem(lootType, lootCount); // Show floating text feedback " +3 Wood " const screenPos = this.iso.toScreen(gridPos.x, gridPos.y); const txt = this.scene.add.text( screenPos.x + this.scene.terrainOffsetX, screenPos.y + this.scene.terrainOffsetY - 30, `+${lootCount} ${lootType.toUpperCase()}`, { fontSize: '14px', fill: '#ffff00', stroke: '#000', strokeThickness: 2 } ); txt.setOrigin(0.5); this.scene.tweens.add({ targets: txt, y: txt.y - 40, alpha: 0, duration: 1000, onComplete: () => txt.destroy() }); } } else if (result === 'hit') { // Play hit sound if (this.scene.soundManager) this.scene.soundManager.playChop(); } } } handleDecorationClick(gridX, gridY) { if (!this.scene.player) return; // Check distance const playerPos = this.scene.player.getPosition(); const dist = Phaser.Math.Distance.Between(playerPos.x, playerPos.y, gridX, gridY); if (dist > 3.0) { // Slightly increased radius for easier clicking console.log('Too far:', dist.toFixed(1)); return; } // Get Active Tool let activeTool = null; const uiScene = this.scene.scene.get('UIScene'); const invSys = this.scene.inventorySystem; if (uiScene && invSys) { const selectedIdx = uiScene.selectedSlot; const slotData = invSys.slots[selectedIdx]; if (slotData) activeTool = slotData.type; } // REUSE LOGIC const id = `${gridX},${gridY}`; if (this.scene.terrainSystem.decorationsMap.has(id)) { const decor = this.scene.terrainSystem.decorationsMap.get(id); // Calculate Damage based on Tool let damage = 1; // Default hand damage // Tool Logic if (decor.type === 'tree' && activeTool === 'axe') { damage = 3; // Axe destroys tree fast } else if ((decor.type === 'bush' || decor.type === 'stone') && activeTool === 'pickaxe') { damage = 3; // Pickaxe destroys stone fast } // Apply damage const result = this.scene.terrainSystem.damageDecoration(gridX, gridY, damage); if (result === 'destroyed') { // Play proper sound if (decor.type === 'tree') { if (this.scene.soundManager) this.scene.soundManager.playChop(); } else { if (this.scene.soundManager) this.scene.soundManager.playChop(); } // AUTO-LOOT directly to Inventory let lootType = 'wood'; // Default let lootCount = 1; if (decor.type === 'tree') { lootType = 'wood'; lootCount = 3 + Math.floor(Math.random() * 3); // 3-5 wood } else if (decor.type === 'bush' || decor.type === 'stone') { lootType = 'stone'; lootCount = 2 + Math.floor(Math.random() * 3); // 2-4 stone } else if (decor.type === 'flower') { lootType = 'seeds'; lootCount = 1; } console.log(`🎁 Auto-looted: ${lootCount}x ${lootType}`); if (invSys) { invSys.addItem(lootType, lootCount); // Show floating text feedback const screenPos = this.iso.toScreen(gridX, gridY); const txt = this.scene.add.text( screenPos.x + this.scene.terrainOffsetX, screenPos.y + this.scene.terrainOffsetY - 30, `+${lootCount} ${lootType.toUpperCase()}`, { fontSize: '14px', fill: '#ffff00', stroke: '#000', strokeThickness: 2 } ); txt.setOrigin(0.5); this.scene.tweens.add({ targets: txt, y: txt.y - 40, alpha: 0, duration: 1000, onComplete: () => txt.destroy() }); } } else if (result === 'hit') { if (this.scene.soundManager) this.scene.soundManager.playChop(); } } } spawnLoot(gridX, gridY, type) { console.log(`🎁 Spawning ${type} at ${gridX},${gridY}`); // Convert to Screen const screenPos = this.iso.toScreen(gridX, gridY); const x = screenPos.x + this.scene.terrainOffsetX; const y = screenPos.y + this.scene.terrainOffsetY; // Create simplistic item drop sprite let symbol = '?'; if (type === 'wood') symbol = '🪵'; if (type === 'seeds') symbol = '🌱'; if (type === 'wheat') symbol = '🌾'; if (type === 'hoe') symbol = '🛠️'; const drop = this.scene.add.text(x, y - 20, symbol, { fontSize: '20px' }); drop.setOrigin(0.5); drop.setDepth(this.iso.getDepth(gridX, gridY) + 500); // above tiles // Bounce animation this.scene.tweens.add({ targets: drop, y: y - 40, duration: 500, yoyo: true, ease: 'Sine.easeOut', repeat: -1 }); this.drops.push({ gridX, gridY, sprite: drop, type: type }); } update() { // Check for player pickup if (!this.scene.player) return; const playerPos = this.scene.player.getPosition(); // Filter drops to pick up for (let i = this.drops.length - 1; i >= 0; i--) { const drop = this.drops[i]; // Check if player is ON the drop tile if (Math.abs(drop.gridX - playerPos.x) < 0.8 && Math.abs(drop.gridY - playerPos.y) < 0.8) { // Pick up! console.log('🎒 Picked up:', drop.type); // Play pickup sound if (this.scene.soundManager) this.scene.soundManager.playPickup(); // Add to inventory if (this.scene.inventorySystem) { this.scene.inventorySystem.addItem(drop.type, 1); } // Destroy visual drop.sprite.destroy(); this.drops.splice(i, 1); } } } }