// Player Entity // Igralec z WASD kontrolami in isometrično podporo class Player { constructor(scene, gridX = 50, gridY = 50, offsetX = 0, offsetY = 0) { this.scene = scene; this.gridX = gridX; this.gridY = gridY; // Terrain offset this.offsetX = offsetX; this.offsetY = offsetY; this.iso = new IsometricUtils(48, 24); // Hitrost gibanja this.moveSpeed = 150; // px/s this.gridMoveTime = 200; // ms za premik na eno kocko // Stanje this.isMoving = false; this.direction = 'down'; this.lastDir = { x: 0, y: 1 }; // Default south // Stats this.hp = 100; this.maxHp = 100; this.isDead = false; // Kreira sprite this.createSprite(); // Setup kontrole this.setupControls(); // Space za napad this.scene.input.keyboard.on('keydown-SPACE', () => { if (!this.isDead) this.attack(); }); // Začetna pozicija this.updatePosition(); } takeDamage(amount) { if (this.isDead) return; this.hp -= amount; console.log(`Player HP: ${this.hp}`); // Visual Feedback this.scene.cameras.main.shake(100, 0.01); this.sprite.setTint(0xff0000); this.scene.time.delayedCall(200, () => { if (!this.isDead) this.sprite.clearTint(); }); // Update UI (če obstaja StatsSystem ali UI) if (this.scene.statsSystem) { // Scene specific stats update if linked } if (this.hp <= 0) { this.die(); } } die() { this.isDead = true; this.sprite.setTint(0x555555); this.sprite.setRotation(Math.PI / 2); // Lie down console.log('💀 PLAYER DIED'); // Death Sound if (this.scene.soundManager) { this.scene.soundManager.playDeath(); } // Show Game Over / Reload const txt = this.scene.add.text(this.scene.cameras.main.midPoint.x, this.scene.cameras.main.midPoint.y, 'YOU DIED', { fontSize: '64px', color: '#ff0000', fontStyle: 'bold', stroke: '#000', strokeThickness: 6 }).setOrigin(0.5).setDepth(20000); this.scene.time.delayedCall(3000, () => { window.location.reload(); }); } createSprite() { // NEW: Use player_dreadlocks sprite (isometric 2.5D) let texKey = 'player_dreadlocks'; // Fallback chain if new sprite not loaded if (!this.scene.textures.exists(texKey)) { texKey = this.scene.textures.exists('player_walk') ? 'player_walk' : 'player_sprite'; if (!this.scene.textures.exists(texKey)) { texKey = 'player'; if (!this.scene.textures.exists(texKey)) { TextureGenerator.createPlayerSprite(this.scene, texKey); } } } // Kreira sprite const screenPos = this.iso.toScreen(this.gridX, this.gridY); this.sprite = this.scene.add.sprite( screenPos.x + this.offsetX, screenPos.y + this.offsetY, texKey ); this.sprite.setOrigin(0.5, 1); // Scale based on sprite type if (texKey === 'player_dreadlocks') { this.sprite.setScale(2.5); // MUCH larger for better visibility } else if (texKey === 'player_walk') { this.sprite.setScale(2.5); // Old animated sprite } else { this.sprite.setScale(1.2); // Old static sprite } // --- HAND / HELD ITEM SPRITE --- this.handSprite = this.scene.add.sprite( screenPos.x + this.offsetX + 10, screenPos.y + this.offsetY - 25, 'item_axe' ); this.handSprite.setOrigin(0.5, 0.5); this.handSprite.setScale(0.25); this.handSprite.setVisible(false); this.updateDepth(); } setupControls() { this.keys = this.scene.input.keyboard.addKeys({ up: Phaser.Input.Keyboard.KeyCodes.W, down: Phaser.Input.Keyboard.KeyCodes.S, left: Phaser.Input.Keyboard.KeyCodes.A, right: Phaser.Input.Keyboard.KeyCodes.D }); // Gamepad Events this.scene.input.gamepad.on('connected', (pad) => { this.setupGamepadEvents(pad); }); if (this.scene.input.gamepad.total > 0) { this.setupGamepadEvents(this.scene.input.gamepad.getPad(0)); } } setupGamepadEvents(pad) { if (!pad) return; console.log('🎮 Gamepad Connected:', pad.id); pad.on('down', (index) => { if (this.isDead) return; // A Button (0) - Attack if (index === 0) { this.attack(); } // X Button (2) - Interact (Pick/Tame) if (index === 2) { const targetX = this.gridX + this.lastDir.x; const targetY = this.gridY + this.lastDir.y; if (this.scene.interactionSystem) { this.scene.interactionSystem.handleInteraction(targetX, targetY); } } // Y Button (3) - Crafting if (index === 3) { // Zahteva, da UIScene posluša ta event, ali pa direktni klic const ui = this.scene.scene.get('UIScene'); if (ui) ui.toggleCrafting(); } // B Button (1) - Inventory if (index === 1) { const ui = this.scene.scene.get('UIScene'); if (ui) ui.toggleInventory(); } }); } attack() { console.log('⚔️ Player Attack!'); // Attack Sound if (this.scene.soundManager) { this.scene.soundManager.playAttack(); } if (this.scene.interactionSystem) { const targetX = this.gridX + this.lastDir.x; const targetY = this.gridY + this.lastDir.y; this.scene.interactionSystem.handleInteraction(targetX, targetY, true); // true = attackMode } // Animation this.scene.tweens.add({ targets: this.handSprite, angle: 45, // Swing yoyo: true, duration: 100 }); // Player lunge const lungeX = this.sprite.x + (this.lastDir.x * 10); const lungeY = this.sprite.y + (this.lastDir.y * 5); this.scene.tweens.add({ targets: this.sprite, x: lungeX, y: lungeY, yoyo: true, duration: 50 }); } update(delta) { if (this.isMoving) { this.updateDepth(); } if (!this.isMoving) { this.handleInput(); } this.updateHeldItem(); // SPACE KEY - Farming Action if (this.keys.space && Phaser.Input.Keyboard.JustDown(this.keys.space)) { this.handleFarmingAction(); } } updateHeldItem() { const uiScene = this.scene.scene.get('UIScene'); const invSys = this.scene.inventorySystem; if (uiScene && invSys) { const selectedIdx = uiScene.selectedSlot; const slot = invSys.slots[selectedIdx]; if (slot && (slot.type === 'axe' || slot.type === 'pickaxe' || slot.type === 'hoe' || slot.type === 'sword')) { const texKey = `item_${slot.type}`; if (this.scene.textures.exists(texKey)) { this.handSprite.setTexture(texKey); this.handSprite.setVisible(true); } else { this.handSprite.setVisible(false); } } else { this.handSprite.setVisible(false); } } } handleInput() { let targetX = this.gridX; let targetY = this.gridY; let moved = false; let facingRight = !this.sprite.flipX; // Determine inputs let up = this.keys.up.isDown; let down = this.keys.down.isDown; let left = this.keys.left.isDown; let right = this.keys.right.isDown; // Check Virtual Joystick inputs (from UIScene) const ui = this.scene.scene.get('UIScene'); if (ui && ui.virtualJoystick) { if (ui.virtualJoystick.up) up = true; if (ui.virtualJoystick.down) down = true; if (ui.virtualJoystick.left) left = true; if (ui.virtualJoystick.right) right = true; } // Check Gamepad Input (Xbox Controller) if (this.scene.input.gamepad && this.scene.input.gamepad.total > 0) { const pad = this.scene.input.gamepad.getPad(0); if (pad) { const threshold = 0.3; if (pad.leftStick.y < -threshold) up = true; if (pad.leftStick.y > threshold) down = true; if (pad.leftStick.x < -threshold) left = true; if (pad.leftStick.x > threshold) right = true; // D-Pad support if (pad.up) up = true; if (pad.down) down = true; if (pad.left) left = true; if (pad.right) right = true; } } // Apply let dx = 0; let dy = 0; if (up) { dx = -1; dy = 0; moved = true; facingRight = false; } else if (down) { dx = 1; dy = 0; moved = true; facingRight = true; } if (left) { dx = 0; dy = 1; moved = true; facingRight = false; } else if (right) { dx = 0; dy = -1; moved = true; facingRight = true; } // Update target targetX = this.gridX + dx; targetY = this.gridY + dy; // Update Facing Direction and Last Dir if (moved) { // Keep diagonal input clean or prioritize one axis? // Just use the calculated dx/dy. // Note: If both UP and LEFT pressed, logic above overwrites dx/dy. // Let's refine to allow diagonal accumulation if needed, but existing logic prioritized axis. // Current logic: RIGHT/LEFT overwrites UP/DOWN. This is fine for now. this.lastDir = { x: dx, y: dy }; this.sprite.setFlipX(!facingRight); // Hand offset const handOffset = facingRight ? 10 : -10; this.handSprite.setX(this.sprite.x + handOffset); this.handSprite.setFlipX(!facingRight); } // Collision Check const terrainSystem = this.scene.terrainSystem; if (moved && terrainSystem) { if (this.iso.isInBounds(targetX, targetY, terrainSystem.width, terrainSystem.height)) { const tile = terrainSystem.tiles[targetY][targetX]; let isPassable = true; // TILE COLLISION - Preveri solid property PRVO if (tile.solid === true) { console.log('⛔ Blocked by solid tile property'); isPassable = false; } // Nato preveri tip (fallback) const solidTileTypes = [ 'water', // Voda 'MINE_WALL', // Rudniški zidovi 'WALL_EDGE', // Robovi zidov (DODANO) 'ORE_STONE', // Kamnita ruda (dokler ni izkopana) 'ORE_IRON', // Železna ruda 'lava', // Lava (če bo dodana) 'void' // Praznina // Opomba: PAVEMENT je WALKABLE (igralec lahko hodi po cesti) ]; const tileName = tile.type.name || tile.type; if (isPassable && solidTileTypes.includes(tileName)) { console.log('⛔ Blocked by solid tile:', tileName); isPassable = false; } // DECORATION COLLISION - Trdni objekti const key = `${targetX},${targetY}`; if (terrainSystem.decorationsMap.has(key)) { const decor = terrainSystem.decorationsMap.get(key); // Preverimo decor.solid property (set by TerrainSystem.addDecoration) if (decor.solid === true) { console.log('⛔ BLOCKED by solid decoration:', decor.type); isPassable = false; } } if (isPassable) { this.moveToGrid(targetX, targetY); } } } } moveToGrid(targetX, targetY) { this.isMoving = true; this.gridX = targetX; this.gridY = targetY; // Footstep Sound if (this.scene.soundManager) { this.scene.soundManager.playFootstep(); } const targetScreen = this.iso.toScreen(targetX, targetY); // Play walk animation - SAFE CHECK if (this.sprite.texture.key === 'player_dreadlocks') { if (this.scene.anims.exists('player_dreadlocks_walk')) { this.sprite.play('player_dreadlocks_walk', true); } } else if (this.sprite.texture.key === 'player_walk') { if (this.scene.anims.exists('player_walk_anim')) { this.sprite.play('player_walk_anim', true); } } this.scene.tweens.add({ targets: [this.sprite, this.handSprite], x: '+=' + (targetScreen.x + this.offsetX - this.sprite.x), y: '+=' + (targetScreen.y + this.offsetY - this.sprite.y), duration: this.gridMoveTime, ease: 'Linear', onComplete: () => { this.isMoving = false; this.updatePosition(); // Stop animation if (this.sprite.texture.key === 'player_dreadlocks') { this.sprite.stop(); this.sprite.setFrame(0); } else if (this.sprite.texture.key === 'player_walk') { this.sprite.stop(); this.sprite.setFrame(0); } } }); this.updateDepth(); } updatePosition() { const screenPos = this.iso.toScreen(this.gridX, this.gridY); // Pixel-perfect positioning const x = Math.round(screenPos.x + this.offsetX); const y = Math.round(screenPos.y + this.offsetY); this.sprite.setPosition(x, y); const facingRight = !this.sprite.flipX; const handOffset = facingRight ? 10 : -10; this.handSprite.setPosition( Math.round(x + handOffset), Math.round(y - 15) ); this.updateDepth(); } updateDepth() { if (!this.sprite) return; // Optimization: Create dirty check if (this.lastDepthY === undefined || Math.abs(this.sprite.y - this.lastDepthY) > 0.1) { // Uporabi LAYER_OBJECTS da se pravilno sortira z drevesi/kamni const layerBase = this.iso.LAYER_OBJECTS || 200000; const depth = layerBase + this.sprite.y; this.sprite.setDepth(depth); if (this.handSprite) this.handSprite.setDepth(depth + 1); this.lastDepthY = this.sprite.y; } } getPosition() { return { x: this.gridX, y: this.gridY }; } getScreenPosition() { return { x: this.sprite.x, y: this.sprite.y }; } destroy() { if (this.sprite) this.sprite.destroy(); } dieAnimation() { this.sprite.setTint(0xff0000); this.scene.tweens.add({ targets: this.sprite, angle: 90, duration: 500, ease: 'Bounce.easeOut' }); } respawn() { this.sprite.clearTint(); this.sprite.angle = 0; } handleFarmingAction() { if (!this.scene.farmingSystem) return; const uiScene = this.scene.scene.get('UIScene'); const invSys = this.scene.inventorySystem; if (!uiScene || !invSys) return; const selectedIdx = uiScene.selectedSlot; const slot = invSys.slots[selectedIdx]; const itemType = slot ? slot.type : null; // Get tile player is standing on const gridX = this.gridX; const gridY = this.gridY; // HOE - Till soil if (itemType === 'hoe') { const success = this.scene.farmingSystem.tillSoil(gridX, gridY); if (success) { console.log('✅ Tilled soil!'); // TODO: Play dig sound // TODO: Tool swing animation } return; } // SEEDS - Plant if (itemType === 'carrot' || itemType === 'wheat' || itemType === 'item_seeds') { const cropType = itemType === 'item_seeds' ? 'carrot' : itemType; const success = this.scene.farmingSystem.plantSeed(gridX, gridY, cropType); if (success) { invSys.removeItem(itemType, 1); console.log('🌱 Planted seed!'); // TODO: Play plant sound } return; } // EMPTY HAND - Harvest if (!itemType) { const success = this.scene.farmingSystem.harvestCrop(gridX, gridY); if (success) { console.log('🌾 Harvested crop!'); // TODO: Play harvest sound // TODO: Screen shake } return; } } }