FAZA 3: Add 3 NPCs with random walk AI (zombie, villager, merchant)

This commit is contained in:
2025-12-06 18:18:23 +01:00
parent 9389d4e467
commit 34a2d07538
5 changed files with 263 additions and 7 deletions

147
src/entities/NPC.js Normal file
View File

@@ -0,0 +1,147 @@
// NPC Entity
// NPC z random walk AI in isometrično podporo
class NPC {
constructor(scene, gridX, gridY, offsetX = 0, offsetY = 0, type = 'zombie') {
this.scene = scene;
this.gridX = gridX;
this.gridY = gridY;
this.type = type;
// Terrain offset
this.offsetX = offsetX;
this.offsetY = offsetY;
this.iso = new IsometricUtils(48, 24);
// Random walk paramters
this.moveSpeed = 100; // px/s (počasnejše od igralca)
this.gridMoveTime = 300; // ms za premik (počasneje)
// Stanje
this.isMoving = false;
this.pauseTime = 0;
this.maxPauseTime = 2000; // Pavza med premiki (2s)
// Kreira sprite
this.createSprite();
// Začetna pozicija
this.updatePosition();
// Naključna začetna pavza
this.pauseTime = Math.random() * this.maxPauseTime;
}
createSprite() {
// Generiraj NPC teksturo glede na tip
const texKey = `npc_${this.type}`;
if (!this.scene.textures.exists(texKey)) {
TextureGenerator.createNPCSprite(this.scene, texKey, this.type);
}
// 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); // Anchor na dnu sprite-a
// Depth sorting
this.updateDepth();
}
update(delta) {
if (this.isMoving) {
return; // Že se premika
}
// Random walk - pavza med premiki
this.pauseTime += delta;
if (this.pauseTime >= this.maxPauseTime) {
this.performRandomWalk();
this.pauseTime = 0;
}
}
performRandomWalk() {
// Naključna smer (NSEW + možnost obstati)
const directions = [
{ x: -1, y: 0 }, // North-West
{ x: 1, y: 0 }, // South-East
{ x: 0, y: -1 }, // South-West
{ x: 0, y: 1 }, // North-East
{ x: 0, y: 0 } // Stay (30% možnost)
];
const dir = Phaser.Math.RND.pick(directions);
const targetX = this.gridX + dir.x;
const targetY = this.gridY + dir.y;
// Preveri kolizijo z robovi
const terrainSystem = this.scene.terrainSystem;
if (terrainSystem && this.iso.isInBounds(targetX, targetY, terrainSystem.width, terrainSystem.height)) {
// Preveri da ni ista pozicija kot igralec
if (this.scene.player) {
const playerPos = this.scene.player.getPosition();
if (targetX === playerPos.x && targetY === playerPos.y) {
return; // Ne premakni se na igralca
}
}
if (dir.x !== 0 || dir.y !== 0) {
this.moveToGrid(targetX, targetY);
}
}
}
moveToGrid(targetX, targetY) {
this.isMoving = true;
this.gridX = targetX;
this.gridY = targetY;
const targetScreen = this.iso.toScreen(targetX, targetY);
// Tween za smooth gibanje
this.scene.tweens.add({
targets: this.sprite,
x: targetScreen.x + this.offsetX,
y: targetScreen.y + this.offsetY,
duration: this.gridMoveTime,
ease: 'Linear',
onComplete: () => {
this.isMoving = false;
}
});
// Posodobi depth
this.updateDepth();
}
updatePosition() {
const screenPos = this.iso.toScreen(this.gridX, this.gridY);
this.sprite.setPosition(
screenPos.x + this.offsetX,
screenPos.y + this.offsetY
);
this.updateDepth();
}
updateDepth() {
const depth = this.iso.getDepth(this.gridX, this.gridY);
this.sprite.setDepth(depth + 1000); // +1000 da je nad terenom
}
getPosition() {
return { x: this.gridX, y: this.gridY };
}
destroy() {
if (this.sprite) {
this.sprite.destroy();
}
}
}