diff --git a/dev_plan.md b/dev_plan.md
index 49fce8d..4d24b22 100644
--- a/dev_plan.md
+++ b/dev_plan.md
@@ -82,7 +82,7 @@
---
## FAZA 3: NPC-ji in Dekoracije
-**Status:** ⏸️ Čaka
+**Status:** ⏳ V TEKU
### Opravila:
- [ ] Dodaj NPC-je (3 na velikost 100x100)
diff --git a/index.html b/index.html
index 33d88e1..7201325 100644
--- a/index.html
+++ b/index.html
@@ -44,6 +44,7 @@
+
diff --git a/src/entities/NPC.js b/src/entities/NPC.js
new file mode 100644
index 0000000..aaa4280
--- /dev/null
+++ b/src/entities/NPC.js
@@ -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();
+ }
+ }
+}
diff --git a/src/scenes/GameScene.js b/src/scenes/GameScene.js
index 7763c81..208b314 100644
--- a/src/scenes/GameScene.js
+++ b/src/scenes/GameScene.js
@@ -5,6 +5,7 @@ class GameScene extends Phaser.Scene {
this.terrainSystem = null;
this.terrainContainer = null;
this.player = null;
+ this.npcs = []; // Array za NPCje
}
create() {
@@ -31,6 +32,16 @@ class GameScene extends Phaser.Scene {
console.log('👤 Initializing player...');
this.player = new Player(this, 50, 50, this.terrainOffsetX, this.terrainOffsetY);
+ // Dodaj 3 NPCje - random pozicije
+ console.log('🧟 Initializing NPCs...');
+ const npcTypes = ['zombie', 'villager', 'merchant'];
+ for (let i = 0; i < 3; i++) {
+ const randomX = Phaser.Math.Between(20, 80);
+ const randomY = Phaser.Math.Between(20, 80);
+ const npc = new NPC(this, randomX, randomY, this.terrainOffsetX, this.terrainOffsetY, npcTypes[i]);
+ this.npcs.push(npc);
+ }
+
// Kamera sledi igralcu
this.cameras.main.startFollow(this.player.sprite, true, 0.1, 0.1);
@@ -60,7 +71,7 @@ class GameScene extends Phaser.Scene {
this.fpsText.setScrollFactor(0);
this.fpsText.setDepth(1000);
- console.log('✅ GameScene ready - FAZA 2!');
+ console.log('✅ GameScene ready - FAZA 3!');
}
setupCamera() {
@@ -91,7 +102,7 @@ class GameScene extends Phaser.Scene {
const width = this.cameras.main.width;
// Naslov
- const title = this.add.text(width / 2, 20, 'FAZA 2: Igralec in Gibanje', {
+ const title = this.add.text(width / 2, 20, 'FAZA 3: NPC-ji in Dekoracije', {
fontFamily: 'Courier New',
fontSize: '20px',
fill: '#00ff41',
@@ -127,6 +138,11 @@ class GameScene extends Phaser.Scene {
this.player.update(delta);
}
+ // Update NPCs
+ for (const npc of this.npcs) {
+ npc.update(delta);
+ }
+
// Update FPS
if (this.fpsText) {
this.fpsText.setText(`FPS: ${Math.round(this.game.loop.actualFps)}`);
@@ -146,13 +162,12 @@ class GameScene extends Phaser.Scene {
// Debug info update
if (this.debugText && this.player) {
const playerPos = this.player.getPosition();
- const screenPos = this.player.getScreenPosition();
this.debugText.setText(
- `FAZA 2 - Player Movement\n` +
+ `FAZA 3 - NPCs & Decorations\n` +
`Zoom: ${cam.zoom.toFixed(2)}\n` +
- `Player Grid: (${playerPos.x}, ${playerPos.y})\n` +
- `Player Screen: (${Math.round(screenPos.x)}, ${Math.round(screenPos.y)})`
+ `Player: (${playerPos.x}, ${playerPos.y})\n` +
+ `NPCs: ${this.npcs.length}`
);
}
}
diff --git a/src/utils/TextureGenerator.js b/src/utils/TextureGenerator.js
index 3bda746..8eac0de 100644
--- a/src/utils/TextureGenerator.js
+++ b/src/utils/TextureGenerator.js
@@ -197,4 +197,97 @@ class TextureGenerator {
pixel(ox + x, oy + 16, outlineColor);
}
}
+
+ // Generiraj NPC sprite (32x32px pixel art)
+ static createNPCSprite(scene, key = 'npc', type = 'zombie') {
+ const size = 32;
+ const canvas = scene.textures.createCanvas(key, size, size);
+ const ctx = canvas.getContext();
+
+ ctx.clearRect(0, 0, size, size);
+
+ const pixel = (x, y, color) => {
+ ctx.fillStyle = color;
+ ctx.fillRect(x, y, 1, 1);
+ };
+
+ const ox = 12;
+ const oy = 4;
+ const outlineColor = '#000000';
+
+ // Različne barve glede na tip
+ let skinColor, shirtColor, pantsColor;
+
+ switch (type) {
+ case 'zombie':
+ skinColor = '#9ACD32'; // Zelena koža
+ shirtColor = '#8B4513'; // Rjava raztrgana srajca
+ pantsColor = '#4A4A4A'; // Temno siva
+ break;
+ case 'villager':
+ skinColor = '#FFDBAC';
+ shirtColor = '#4169E1'; // Modra srajca
+ pantsColor = '#654321'; // Rjave hlače
+ break;
+ case 'merchant':
+ skinColor = '#FFDBAC';
+ shirtColor = '#DAA520'; // Zlata/rumena srajca
+ pantsColor = '#2F4F4F'; // Temno zelene hlače
+ break;
+ default:
+ skinColor = '#FFDBAC';
+ shirtColor = '#888888';
+ pantsColor = '#666666';
+ }
+
+ // Glava (brez klobuka za NPCje)
+ for (let y = 2; y < 6; y++) {
+ pixel(ox + 0, oy + y, outlineColor);
+ for (let x = 1; x < 7; x++) {
+ pixel(ox + x, oy + y, skinColor);
+ }
+ pixel(ox + 7, oy + y, outlineColor);
+ }
+
+ // Oči
+ pixel(ox + 2, oy + 4, outlineColor);
+ pixel(ox + 5, oy + 4, outlineColor);
+
+ // Telo - srajca
+ for (let y = 6; y < 11; y++) {
+ pixel(ox + 0, oy + y, outlineColor);
+ for (let x = 1; x < 7; x++) {
+ pixel(ox + x, oy + y, shirtColor);
+ }
+ pixel(ox + 7, oy + y, outlineColor);
+ }
+
+ // Roke
+ for (let y = 7; y < 10; y++) {
+ pixel(ox - 1, oy + y, skinColor);
+ pixel(ox - 2, oy + y, outlineColor);
+ pixel(ox + 8, oy + y, skinColor);
+ pixel(ox + 9, oy + y, outlineColor);
+ }
+
+ // Noge - hlače
+ for (let y = 11; y < 16; y++) {
+ pixel(ox + 0, oy + y, outlineColor);
+ pixel(ox + 1, oy + y, pantsColor);
+ pixel(ox + 2, oy + y, pantsColor);
+ pixel(ox + 3, oy + y, outlineColor);
+ pixel(ox + 4, oy + y, outlineColor);
+ pixel(ox + 5, oy + y, pantsColor);
+ pixel(ox + 6, oy + y, pantsColor);
+ pixel(ox + 7, oy + y, outlineColor);
+ }
+
+ // Dno nog
+ for (let x = 0; x < 8; x++) {
+ pixel(ox + x, oy + 16, outlineColor);
+ }
+
+ canvas.refresh();
+ return canvas;
+ }
}