Files
novafarma/src/entities/Player.js

311 lines
9.0 KiB
JavaScript

// 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
// Kreira sprite
this.createSprite();
// Setup kontrole
this.setupControls();
// Space za napad
this.scene.input.keyboard.on('keydown-SPACE', () => {
this.attack();
});
// Začetna pozicija
this.updatePosition();
}
createSprite() {
// Prefer animated sprite if available
let texKey = 'player_walk'; // Spritesheet
let isAnimated = this.scene.textures.exists(texKey);
if (!isAnimated) {
texKey = this.scene.textures.exists('player_sprite') ? 'player_sprite' : '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 logic
if (isAnimated) {
this.sprite.setScale(1.5);
} else {
this.sprite.setScale(0.3);
}
// --- 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
});
}
attack() {
console.log('⚔️ Player Attack!');
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();
}
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;
// WASD
let dx = 0;
let dy = 0;
if (this.keys.up.isDown) {
dx = -1; dy = 0;
moved = true;
facingRight = false;
} else if (this.keys.down.isDown) {
dx = 1; dy = 0;
moved = true;
facingRight = true;
}
if (this.keys.left.isDown) {
dx = 0; dy = 1;
moved = true;
facingRight = false;
} else if (this.keys.right.isDown) {
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;
if (tile.type.name === 'water') isPassable = false;
const key = `${targetX},${targetY}`;
if (terrainSystem.decorationsMap.has(key)) {
const decor = terrainSystem.decorationsMap.get(key);
const solidTypes = ['tree', 'stone', 'bush', 'wall', 'ruin', 'fence', 'house', 'gravestone'];
if (solidTypes.includes(decor.type)) {
console.log('⛔ Blocked by:', decor.type);
isPassable = false;
}
}
if (isPassable) {
this.moveToGrid(targetX, targetY);
}
}
}
}
moveToGrid(targetX, targetY) {
this.isMoving = true;
this.gridX = targetX;
this.gridY = targetY;
const targetScreen = this.iso.toScreen(targetX, targetY);
if (this.sprite.texture.key === 'player_walk') {
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();
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);
this.sprite.setPosition(
screenPos.x + this.offsetX,
screenPos.y + this.offsetY
);
const facingRight = !this.sprite.flipX;
const handOffset = facingRight ? 10 : -10;
this.handSprite.setPosition(
screenPos.x + this.offsetX + handOffset,
screenPos.y + this.offsetY - 15
);
this.updateDepth();
}
updateDepth() {
if (this.sprite) {
this.sprite.setDepth(this.sprite.y);
if (this.handSprite) this.handSprite.setDepth(this.sprite.y + 1);
}
}
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;
}
}