feat: Faza 1 Gameplay Loop - Kista, Sekanje, Gradnja, Kmetovanje, Dnevnik, Zombiji

- Starter Chest (Kista): Nov sprite asset (chest_closed.png), zamenjal rectangles z dejansko sliko, dodal axe reward ob odprtju
- Sekanje dreves: Zahteva Sekiro v rokah, padec drevesa odvrze 3 kose lesa (pickup sistem), unlock dnevniska vnosa
- Gradnja Barikad [B]: Nov wooden_fence.png asset, BuildingSystem posodobljen z leseno barikado (cena: 2 les), ghost preview z grid snappingom, deductItem() API
- Farming zanka: Popravljen seed property v InventorySystem, unlock ob prvem sajenju in zetvi
- Kaijev Dnevnik [J]: Novi JournalSystem.js s 6 vnosi, gotski UI z zlatimi okviri, animirano odpiranje, odklepanje ob napredku
- Zombie nochni napadi: Stopnjevano po nochih (max 8, hitrost do 65px/s, krajsi spawn delay), unlock dnevnika ob prvi nochi
- InventorySystem: Popravljen bug kjer tools niso bili dodani v hotbar, dodan axe in wood v ITEM_DEFS, nova deductItem() funkcija
- Pomoznna remove_bg.py skripta za odstranjevanje belega ozadja assetov
This commit is contained in:
2026-03-13 20:11:46 +01:00
parent 188bd769cf
commit b660429e3c
13 changed files with 860 additions and 131 deletions

BIN
assets/.DS_Store vendored

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 472 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 298 KiB

View File

@@ -0,0 +1,25 @@
import sys
from PIL import Image
def process_image(input_path, output_path):
img = Image.open(input_path).convert("RGBA")
datas = img.getdata()
newData = []
# Convert white background to transparent
for item in datas:
# Check if the pixel color is white or close to white (tolerance)
if item[0] >= 240 and item[1] >= 240 and item[2] >= 240:
newData.append((255, 255, 255, 0)) # Replace with transparent
else:
newData.append(item)
img.putdata(newData)
img.save(output_path, "PNG")
print(f"Saved {output_path}")
if __name__ == "__main__":
if len(sys.argv) < 3:
print("Usage: python remove_bg.py <input> <output>")
else:
process_image(sys.argv[1], sys.argv[2])

View File

@@ -4,6 +4,8 @@ import WaterSystem from '../systems/WaterSystem.js';
import DayNightSystem from '../systems/DayNightSystem.js'; import DayNightSystem from '../systems/DayNightSystem.js';
import InventorySystem from '../systems/InventorySystem.js'; import InventorySystem from '../systems/InventorySystem.js';
import ZombieSystem from '../systems/ZombieSystem.js'; import ZombieSystem from '../systems/ZombieSystem.js';
import TwinBondSystem from '../systems/TwinBondSystem.js';
import JournalSystem from '../systems/JournalSystem.js';
export default class GrassSceneClean extends Phaser.Scene { export default class GrassSceneClean extends Phaser.Scene {
constructor() { constructor() {
@@ -117,6 +119,7 @@ export default class GrassSceneClean extends Phaser.Scene {
this.load.image('campfire', 'DEMO_FAZA1/Environment/taborni_ogenj.png'); this.load.image('campfire', 'DEMO_FAZA1/Environment/taborni_ogenj.png');
this.load.image('tent', 'DEMO_FAZA1/Environment/sotor.png'); this.load.image('tent', 'DEMO_FAZA1/Environment/sotor.png');
this.load.image('sleeping_bag', 'DEMO_FAZA1/Items/spalna_vreca.png'); this.load.image('sleeping_bag', 'DEMO_FAZA1/Items/spalna_vreca.png');
this.load.image('chest_closed', 'DEMO_FAZA1/Buildings/chest_closed.png');
// 7. NEW: Gronk & Structures // 7. NEW: Gronk & Structures
this.load.spritesheet('gronk', 'DEMO_FAZA1/Characters/gronk_walk_sheet.png', { this.load.spritesheet('gronk', 'DEMO_FAZA1/Characters/gronk_walk_sheet.png', {
@@ -137,6 +140,9 @@ export default class GrassSceneClean extends Phaser.Scene {
// 11. Zombie System Assets // 11. Zombie System Assets
ZombieSystem.preload(this); ZombieSystem.preload(this);
// 12. Building System Assets
this.load.image('wooden_fence', 'DEMO_FAZA1/Buildings/wooden_fence.png');
} }
// =================================================================== // ===================================================================
@@ -202,6 +208,35 @@ export default class GrassSceneClean extends Phaser.Scene {
// SMOOTH RENDERING: Linear filter (no pixelation) // SMOOTH RENDERING: Linear filter (no pixelation)
sprite.texture.setFilter(Phaser.Textures.FilterMode.LINEAR); sprite.texture.setFilter(Phaser.Textures.FilterMode.LINEAR);
// PHYSICS & INTERACTION
this.physics.add.existing(sprite, true); // true = static body
if (sprite.body) {
// Trunk size roughly matching the visual base
sprite.body.setSize(sprite.width * scale * 0.4, 40);
sprite.body.setOffset(sprite.width * 0.3, sprite.height - 40);
}
sprite.health = 3;
sprite.isChopping = false;
sprite.setInteractive({ useHandCursor: true });
sprite.on('pointerdown', () => {
const dist = Phaser.Math.Distance.Between(this.scene?.kai?.x || this.kai?.x, this.scene?.kai?.y || this.kai?.y, sprite.x, sprite.y);
if (dist < 150) {
// Preveri če ima igralec izbrano sekiro v rokah
const activeItemKey = this.inventorySystem?.hotbar[this.inventorySystem?.activeSlot];
const activeItemDef = activeItemKey ? this.inventorySystem?.ITEM_DEFS[activeItemKey] : null;
if (activeItemDef && activeItemDef.tool === 'axe') {
this.chopTree(sprite);
} else {
if (this.inventorySystem) {
this.inventorySystem._showMsg('Rabiš Sekiro v rokah!', '#ff4444');
}
}
}
});
return sprite; return sprite;
} }
@@ -357,17 +392,13 @@ export default class GrassSceneClean extends Phaser.Scene {
// FAILSAFE 2: Use simple Rectangles instead of TileSprites/Textures to STOP OOM. // FAILSAFE 2: Use simple Rectangles instead of TileSprites/Textures to STOP OOM.
// We will add waves later if stability allows. // We will add waves later if stability allows.
const viewW = this.scale.width || 1280; // Ustvarimo dva velika modra pravokotnika kot 'bazen', v katerem je otok
const viewH = this.scale.height || 720; this.oceanLayer1 = this.add.rectangle(WORLD_W / 2, WORLD_H / 2, WORLD_W + 2000, WORLD_H + 2000, 0x004488)
this.oceanLayer1 = this.add.rectangle(viewW / 2, viewH / 2, viewW + 100, viewH + 100, 0x004488)
.setScrollFactor(0)
.setDepth(-290) .setDepth(-290)
.setAlpha(0.5); .setAlpha(0.5);
// Optional: Second layer for depth effect // Optional: Second layer for depth effect
this.oceanLayer2 = this.add.rectangle(viewW / 2, viewH / 2, viewW + 100, viewH + 100, 0x002244) this.oceanLayer2 = this.add.rectangle(WORLD_W / 2, WORLD_H / 2, WORLD_W + 2000, WORLD_H + 2000, 0x002244)
.setScrollFactor(0)
.setDepth(-295) // Behind .setDepth(-295) // Behind
.setAlpha(1.0); .setAlpha(1.0);
@@ -960,34 +991,95 @@ export default class GrassSceneClean extends Phaser.Scene {
this.physics.add.overlap(this.kai, this.sleepingBag, () => { this.physics.add.overlap(this.kai, this.sleepingBag, () => {
if (this.lastCheckpointTime && this.time.now - this.lastCheckpointTime < 5000) return; if (this.lastCheckpointTime && this.time.now - this.lastCheckpointTime < 5000) return;
this.saveCheckpoint(this.sleepingBag.x, this.sleepingBag.y);
this.lastCheckpointTime = this.time.now;
}); });
*/ */
console.log('SPALNA VREČA ODSTRANJENA'); // ─── SPALNA VREČA & STARTER CHEST (Kišta) ────────────────────────
this.sleepingBag = this.physics.add.image(SPAWN_X + 150, 5600, 'sleeping_bag');
this.sleepingBag.setOrigin(0.5, 1.0);
this.sleepingBag.setScale(0.3);
this.sleepingBag.setDepth(this.sleepingBag.y);
// =================================================================== this.starterChest = this.physics.add.image(SPAWN_X - 100, 5600, 'chest_closed');
// TEST: AUTO-LOADER DEMO this.starterChest.setOrigin(0.5, 1.0);
// =================================================================== this.starterChest.setScale(0.18);
// Place test vegetation and tree to verify grounding system this.starterChest.setDepth(5600);
this.starterChest.body.setSize(this.starterChest.width * 0.18, this.starterChest.height * 0.18);
this.starterChest.body.setImmovable(true);
this.starterChest.opened = false;
const testTileBottom = SPAWN_Y + 200; // Tile bottom Y position let chestText = this.add.text(SPAWN_X - 100, 5600 - 100, '[E] Odpri Kišto', { fontFamily: 'Arial Black', fontSize: '13px', color: '#ffdd00', stroke: '#000', strokeThickness: 4, align: 'center' }).setOrigin(0.5, 0.5);
chestText.setDepth(9999);
// TEST 1: Vegetation (28px height, 4px burial) this.input.keyboard.on('keydown-E', () => {
this.testGrass = this.loadVegetation( if (this.starterChest.opened) return;
SPAWN_X - 100, // Left of Kai const dist = Phaser.Math.Distance.Between(this.kai.x, this.kai.y, this.starterChest.x, this.starterChest.y);
testTileBottom, if (dist < 150) {
'grass_ref_1' this.starterChest.opened = true;
); chestText.setText('📦 Prazno\n(Vse pobrano)');
console.log('🌿 TEST: Grass loaded at 28px with 4px burial'); chestText.setColor('#888888');
// TEST 2: Tree (160px height, 8px burial) this.tweens.add({
this.testTree = this.loadTree( targets: this.starterChest,
SPAWN_X + 100, // Right of Kai y: this.starterChest.y - 20,
testTileBottom, yoyo: true,
'tree_adult_0' duration: 150,
); onComplete: () => {
console.log('🌳 TEST: Oak loaded at 160px with 8px burial'); this.starterChest.setTint(0x888888);
}
});
// Add items directly to Inventory
this.inventorySystem.addItem('axe', 1);
this.inventorySystem.addItem('hoe', 1);
this.inventorySystem.addItem('watering_can', 1);
this.inventorySystem.addItem('wheat_seed', 5);
this.inventorySystem.addItem('carrot_seed', 5);
// UI Message
this.inventorySystem._showMsg('Pobral si Orodje in Semena!', '#44ff44');
if (this.journalSystem) this.journalSystem.unlockEntry('start');
}
});
// ─────────────────────────────────────────────────────────────────
// === RANDOM TREE GENERATION (AUTO-LOADER) ===
this.treesGroup = this.physics.add.staticGroup();
const treeKeys = ['tree_adult_0', 'tree_adult_1', 'tree_adult_2', 'tree_adult_3', 'tree_adult_4', 'tree_adult_5'];
const TREE_COUNT = 40;
for (let i = 0; i < TREE_COUNT; i++) {
// Place on random edge to keep center farming area free
const edge = Math.floor(Math.random() * 4);
let tx, ty;
const PADDING = 200;
switch (edge) {
case 0: // Top
tx = this.islandX + Math.random() * this.islandWidth;
ty = this.islandY + PADDING + Math.random() * 300;
break;
case 1: // Right
tx = this.islandX + this.islandWidth - PADDING - Math.random() * 300;
ty = this.islandY + Math.random() * this.islandHeight;
break;
case 2: // Bottom
tx = this.islandX + Math.random() * this.islandWidth;
ty = this.islandY + this.islandHeight - PADDING - Math.random() * 300;
break;
case 3: // Left
tx = this.islandX + PADDING + Math.random() * 300;
ty = this.islandY + Math.random() * this.islandHeight;
break;
}
let tKey = Phaser.Utils.Array.GetRandom(treeKeys);
let t = this.loadTree(tx, ty, tKey);
t.setMask(this.islandMask);
this.treesGroup.add(t);
}
// Add solid collision against trees!
this.physics.add.collider(this.kai, this.treesGroup);
// =================================================================== // ===================================================================
// Adjust Physics Body for larger size // Adjust Physics Body for larger size
@@ -1105,6 +1197,9 @@ export default class GrassSceneClean extends Phaser.Scene {
console.log('🏗️ Building System initialized — press [B] to open build mode'); console.log('🏗️ Building System initialized — press [B] to open build mode');
// === JOURNAL SYSTEM (Kaijev Dnevnik) ===
this.journalSystem = new JournalSystem(this);
// === ZAŽENIMO UIScene kot overlay (HUD sloj) === // === ZAŽENIMO UIScene kot overlay (HUD sloj) ===
if (!this.scene.isActive('UIScene')) { if (!this.scene.isActive('UIScene')) {
this.scene.launch('UIScene'); this.scene.launch('UIScene');
@@ -1309,11 +1404,8 @@ export default class GrassSceneClean extends Phaser.Scene {
} }
}); });
// Registriraj spalno vrečo kot sleep objekt // Registriraj dejansko spalno vrečo
// (spawn point je center otoka) this.dayNightSystem.registerSleepObject(this.sleepingBag.x, this.sleepingBag.y, this.sleepingBag, 'sleeping_bag');
const sleepX = this.islandX + this.islandWidth / 2;
const sleepY = this.islandY + this.islandHeight / 2;
this.dayNightSystem.registerSleepObject(sleepX, sleepY, null, 'sleeping_bag');
console.log('✅ DayNightSystem ready — [Z] za spanje, 1 min/s!'); console.log('✅ DayNightSystem ready — [Z] za spanje, 1 min/s!');
@@ -1346,6 +1438,10 @@ export default class GrassSceneClean extends Phaser.Scene {
}); });
this.zombieSystem.createAnimations(); this.zombieSystem.createAnimations();
// ─── TWIN BOND (Telepathy) SYSTEM ─────────────────────────
this.twinBondSystem = new TwinBondSystem(this);
console.log('✅ TwinBondSystem ready!');
// Noč → Začni spawnat // Noč → Začni spawnat
this.events.on('nightArrived', () => { this.events.on('nightArrived', () => {
this.zombieSystem.startNightSpawning(); this.zombieSystem.startNightSpawning();
@@ -1434,6 +1530,42 @@ export default class GrassSceneClean extends Phaser.Scene {
tree.destroy(); tree.destroy();
// Odkleni dnevniski vnos ob prvem podrtju drevesa
if (this.journalSystem && !this._firstTreeFelled) {
this._firstTreeFelled = true;
this.time.delayedCall(800, () => {
this.journalSystem.unlockEntry('first_tree');
});
}
// Drop wood visual (3 pieces)
for (let i = 0; i < 3; i++) {
let wx = tx + (Math.random() - 0.5) * 100;
let wy = ty - 50 + (Math.random() - 0.5) * 50;
let w = this.add.text(wx, wy, '🪵', { fontSize: '35px' }).setOrigin(0.5);
w.setDepth(wy); // y-sorting
this.physics.add.existing(w);
w.body.setSize(40, 40);
// Simple jump/pop animation for the drop
this.tweens.add({
targets: w,
y: wy - 40,
duration: 200,
yoyo: true,
ease: 'Quad.easeOut'
});
// Add collision to collect it
this.physics.add.overlap(this.kai, w, () => {
w.destroy();
if (this.inventorySystem) {
this.inventorySystem.addItem('wood', 1);
this.inventorySystem._showMsg('+1 Les', '#ddaa00');
}
});
}
// 3. AUTO-REPLANT (Spawn Grass Clump) // 3. AUTO-REPLANT (Spawn Grass Clump)
this.spawnReplant(tx, ty); // Use generic regrowth for consistency if desired, or keep as specialized spawn this.spawnReplant(tx, ty); // Use generic regrowth for consistency if desired, or keep as specialized spawn
} }
@@ -1795,35 +1927,40 @@ export default class GrassSceneClean extends Phaser.Scene {
}); });
// --- PLAYER MOVEMENT (SMOOTHENED) --- // --- PLAYER MOVEMENT (SMOOTHENED) ---
const speed = 500; if (this.kai.canMove === false) {
const velocity = new Phaser.Math.Vector2(0, 0);
// Input helpers
const left = this.cursors.left.isDown || this.keys.left.isDown;
const right = this.cursors.right.isDown || this.keys.right.isDown;
const up = this.cursors.up.isDown || this.keys.up.isDown;
const down = this.cursors.down.isDown || this.keys.down.isDown;
const space = this.cursors.space.isDown;
if (left) velocity.x = -1;
else if (right) velocity.x = 1;
if (up) velocity.y = -1;
else if (down) velocity.y = 1;
if (velocity.length() > 0) {
velocity.normalize().scale(speed);
this.kai.setVelocity(velocity.x, velocity.y);
// Animation logic
if (Math.abs(velocity.x) > Math.abs(velocity.y)) {
this.kai.play(velocity.x < 0 ? 'walk-left' : 'walk-right', true);
} else {
this.kai.play(velocity.y < 0 ? 'walk-up' : 'walk-down', true);
}
} else {
this.kai.setVelocity(0, 0); this.kai.setVelocity(0, 0);
this.kai.anims.stop(); this.kai.anims.stop();
} else {
const speed = 500;
const velocity = new Phaser.Math.Vector2(0, 0);
// Input helpers
const left = this.cursors.left.isDown || this.keys.left.isDown;
const right = this.cursors.right.isDown || this.keys.right.isDown;
const up = this.cursors.up.isDown || this.keys.up.isDown;
const down = this.cursors.down.isDown || this.keys.down.isDown;
const space = this.cursors.space.isDown;
if (left) velocity.x = -1;
else if (right) velocity.x = 1;
if (up) velocity.y = -1;
else if (down) velocity.y = 1;
if (velocity.length() > 0) {
velocity.normalize().scale(speed);
this.kai.setVelocity(velocity.x, velocity.y);
// Animation logic
if (Math.abs(velocity.x) > Math.abs(velocity.y)) {
this.kai.play(velocity.x < 0 ? 'walk-left' : 'walk-right', true);
} else {
this.kai.play(velocity.y < 0 ? 'walk-up' : 'walk-down', true);
}
} else {
this.kai.setVelocity(0, 0);
this.kai.anims.stop();
}
} }
@@ -1877,6 +2014,11 @@ export default class GrassSceneClean extends Phaser.Scene {
this.zombieSystem.update(this.kai, delta); this.zombieSystem.update(this.kai, delta);
} }
// === TWIN BOND SYSTEM UPDATE ===
if (this.twinBondSystem) {
this.twinBondSystem.update(time, delta);
}
// === FARMING SYSTEM UPDATE === // === FARMING SYSTEM UPDATE ===
if (this.farmingSystem) { if (this.farmingSystem) {
this.farmingSystem.update(this.kai, time); this.farmingSystem.update(this.kai, time);

View File

@@ -30,6 +30,16 @@ export default class BuildingSystem {
// Katalog stavb — tukaj dodajamo nove // Katalog stavb — tukaj dodajamo nove
this.catalogue = [ this.catalogue = [
{
key: 'wooden_fence',
label: 'Lesena Barikada',
scale: 0.5,
colliderW: 80,
colliderH: 20,
colliderOffsetX: -5,
colliderOffsetY: 0,
cost: { item: 'wood', amount: 2 }
},
{ {
key: 'sotor', key: 'sotor',
label: 'Šotor', label: 'Šotor',
@@ -48,24 +58,6 @@ export default class BuildingSystem {
colliderOffsetX: -25, colliderOffsetX: -25,
colliderOffsetY: -20, colliderOffsetY: -20,
}, },
{
key: 'rain_catcher',
label: 'Zbirač dežja',
scale: 2.0,
colliderW: 140,
colliderH: 40,
colliderOffsetX: -70,
colliderOffsetY: -40,
},
{
key: 'foundation_concrete',
label: 'Betonski temelj',
scale: 2.0,
colliderW: 120,
colliderH: 20,
colliderOffsetX: -60,
colliderOffsetY: -20,
},
]; ];
this._setupInput(); this._setupInput();
@@ -99,7 +91,7 @@ export default class BuildingSystem {
if (!this._modeTip) { if (!this._modeTip) {
this._modeTip = this.scene.add.text( this._modeTip = this.scene.add.text(
this.scene.cameras.main.width / 2, 20, this.scene.cameras.main.width / 2, 20,
'🏗️ NAČIN GRADNJE | Klik = postavi | B / Esc = izhod', '🏗️ NAČIN GRADNJE | 🖱️ postavi | B / Esc izhod | R rotacija',
{ {
fontSize: '16px', fontSize: '16px',
color: '#44ffaa', color: '#44ffaa',
@@ -164,12 +156,30 @@ export default class BuildingSystem {
const wx = pointer.worldX; const wx = pointer.worldX;
const wy = pointer.worldY; const wy = pointer.worldY;
this.ghost.setPosition(wx, wy);
// Zelena = na otoku, Rdeča = zunaj // Snapping na grid 32px
const grid = 32;
const finalX = Math.round(wx / grid) * grid;
const finalY = Math.round(wy / grid) * grid;
this.ghost.setPosition(finalX, finalY);
// Preveri surovine in lokacijo
const def = this.catalogue.find(c => c.key === this.selectedKey);
let ok = true;
const b = this.scene.physics.world.bounds; const b = this.scene.physics.world.bounds;
const ok = wx > b.x && wx < b.x + b.width && if (finalX <= b.x || finalX >= b.x + b.width || finalY <= b.y || finalY >= b.y + b.height) {
wy > b.y && wy < b.y + b.height; ok = false;
}
if (def && def.cost && this.scene.inventorySystem) {
const item = this.scene.inventorySystem.items[def.cost.item];
if (!item || item.count < def.cost.amount) {
ok = false;
}
}
this.ghost.setTint(ok ? 0x44ffaa : 0xff4444); this.ghost.setTint(ok ? 0x44ffaa : 0xff4444);
} }
@@ -177,28 +187,31 @@ export default class BuildingSystem {
// PLACE BUILDING (ob kliku) // PLACE BUILDING (ob kliku)
// ========================================== // ==========================================
placeBuilding(worldX, worldY) { placeBuilding(worldX, worldY) {
if (!this.active || !this.selectedKey) return; if (!this.active || !this.selectedKey || !this.ghost) return;
const def = this.catalogue.find(c => c.key === this.selectedKey); const def = this.catalogue.find(c => c.key === this.selectedKey);
if (!def) return; if (!def) return;
const finalX = this.ghost.x;
const finalY = this.ghost.y;
// Validacija — mora biti na otoku // Validacija — mora biti na otoku
const b = this.scene.physics.world.bounds; const b = this.scene.physics.world.bounds;
if (worldX <= b.x || worldX >= b.x + b.width || if (finalX <= b.x || finalX >= b.x + b.width || finalY <= b.y || finalY >= b.y + b.height) {
worldY <= b.y || worldY >= b.y + b.height) { this._flashGhost();
// Flash rdeče
if (this.ghost) {
this.scene.tweens.add({
targets: this.ghost,
alpha: { from: 0.9, to: 0.2 },
duration: 80,
yoyo: true,
repeat: 2,
});
}
return; return;
} }
// Validacija surovin
if (def.cost && this.scene.inventorySystem) {
const item = this.scene.inventorySystem.items[def.cost.item];
if (!this.scene.inventorySystem.deductItem(def.cost.item, def.cost.amount)) {
this._flashGhost();
this.scene.inventorySystem._showMsg(`Premalo ${def.cost.item}!`, '#ff4444');
return;
}
}
// Izračun scale // Izračun scale
const KAI_H = 64; const KAI_H = 64;
const targetH = KAI_H * def.scale; const targetH = KAI_H * def.scale;
@@ -207,18 +220,18 @@ export default class BuildingSystem {
const scale = targetH / srcH; const scale = targetH / srcH;
// Ustvari static physics image // Ustvari static physics image
const building = this.scene.physics.add.staticImage(worldX, worldY, this.selectedKey) const building = this.scene.physics.add.staticImage(finalX, finalY, this.selectedKey)
.setScale(scale) .setScale(scale)
.setOrigin(0.5, 1.0) .setOrigin(0.5, 1.0)
.setDepth(worldY); .setDepth(finalY);
// Physics collider body (samo baza stavbe) // Physics collider body (samo baza stavbe)
building.body.setSize(def.colliderW, def.colliderH); building.body.setSize(def.colliderW, def.colliderH);
building.body.setOffset( building.body.setOffset(
(texture.getSourceImage().width * scale) / 2 + def.colliderOffsetX, def.colliderOffsetX || 0,
srcH * scale + def.colliderOffsetY def.colliderOffsetY || 0
); );
building.body.reset(worldX, worldY); building.body.reset(finalX, finalY);
// Collider med Kai in stavbo // Collider med Kai in stavbo
this.buildingsGroup.add(building, true); this.buildingsGroup.add(building, true);
@@ -234,7 +247,7 @@ export default class BuildingSystem {
}); });
// Flash feedback // Flash feedback
const flash = this.scene.add.rectangle(worldX, worldY - targetH * 0.5, 80, 80, 0x44ffaa, 0.6) const flash = this.scene.add.rectangle(finalX, finalY - targetH * 0.5, 80, 80, 0x44ffaa, 0.6)
.setDepth(99998); .setDepth(99998);
this.scene.tweens.add({ this.scene.tweens.add({
targets: flash, targets: flash,
@@ -243,12 +256,20 @@ export default class BuildingSystem {
onComplete: () => flash.destroy(), onComplete: () => flash.destroy(),
}); });
// Shrani referenco za Y-sort v update() this.placed.push({ key: this.selectedKey, x: finalX, y: finalY });
this.scene._buildingSprites = this.scene._buildingSprites || [];
this.scene._buildingSprites.push(building);
this.placed.push({ key: this.selectedKey, x: worldX, y: worldY }); console.log(`🏗️ Placed: ${this.selectedKey} @ (${Math.round(finalX)}, ${Math.round(finalY)})`);
}
console.log(`🏗️ Placed: ${this.selectedKey} @ (${Math.round(worldX)}, ${Math.round(worldY)})`); _flashGhost() {
if (this.ghost) {
this.scene.tweens.add({
targets: this.ghost,
alpha: { from: 0.9, to: 0.2 },
duration: 80,
yoyo: true,
repeat: 2,
});
}
} }
} }

View File

@@ -48,9 +48,8 @@ export default class DayNightSystem {
// Input [Z] za spanje // Input [Z] za spanje
this.keyZ = scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.Z); this.keyZ = scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.Z);
// Ambient Graphics (overlay za noč) // Ambient Graphics (overlay za noč) - Pokriva celoten svet!
this.ambientOverlay = scene.add.graphics() this.ambientOverlay = scene.add.graphics()
.setScrollFactor(0)
.setDepth(4500); // Pod UIScene (depth 5000+), nad svetom .setDepth(4500); // Pod UIScene (depth 5000+), nad svetom
// Star particles za noč // Star particles za noč
@@ -273,7 +272,10 @@ export default class DayNightSystem {
(r << 16) | (g << 8) | b, (r << 16) | (g << 8) | b,
alpha alpha
); );
this.ambientOverlay.fillRect(0, 0, W, H); // Ker imava izklopljen GPU, .setScrollFactor(0) včasih povzroča
// težave pri zoomu. Zato je NAJSIGURNEJE preprosto narisati čez celoten 'svet' (10000x10000)
// Tako bo "celoten otok" vedno pokrit, neglede na kamero in pozicijo!
this.ambientOverlay.fillRect(-2000, -2000, 15000, 15000);
} }
} }
@@ -282,15 +284,14 @@ export default class DayNightSystem {
// ───────────────────────────────────────────────────────────────────────── // ─────────────────────────────────────────────────────────────────────────
_initStars() { _initStars() {
const W = this.scene.cameras.main.width; const W = this.scene.cameras.main.width;
const H = this.scene.cameras.main.height;
this.starGfx = this.scene.add.graphics() this.starGfx = this.scene.add.graphics()
.setScrollFactor(0)
.setDepth(4499); .setDepth(4499);
for (let i = 0; i < 80; i++) { // Zvezde raztrosimo čez celoten svet (zgoraj)
for (let i = 0; i < 400; i++) {
this.stars.push({ this.stars.push({
x: Math.random() * W, x: -2000 + Math.random() * 12000,
y: Math.random() * H * 0.6, // Samo zgoraj y: -2000 + Math.random() * 8000,
size: 0.5 + Math.random() * 1.5, size: 0.5 + Math.random() * 1.5,
twinkle: Math.random() * Math.PI * 2, twinkle: Math.random() * Math.PI * 2,
speed: 0.5 + Math.random() * 1.5, speed: 0.5 + Math.random() * 1.5,

View File

@@ -323,6 +323,12 @@ export default class FarmingSystem {
this._showFloatingText(plot.x, plot.y - 30, `🌱 ${cropKey}!`, '#88ff88', 18); this._showFloatingText(plot.x, plot.y - 30, `🌱 ${cropKey}!`, '#88ff88', 18);
console.log(`🌱 Posajeno: ${cropKey} @ day ${this.currentDay}`); console.log(`🌱 Posajeno: ${cropKey} @ day ${this.currentDay}`);
// Odkleni dnevniški vnos ob prvem sajenju
if (!this._firstPlant && this.scene.journalSystem) {
this._firstPlant = true;
this.scene.journalSystem.unlockEntry('first_plant');
}
} }
// ───────────────────────────────────────────────────────────────────────── // ─────────────────────────────────────────────────────────────────────────
@@ -421,6 +427,14 @@ export default class FarmingSystem {
}); });
console.log(`🌾 Harvest: ${def.harvestItem} ×${count} = ${value}g`); console.log(`🌾 Harvest: ${def.harvestItem} ×${count} = ${value}g`);
// Odkleni dnevniški vnos ob prvi žetvi
if (!this._firstHarvest && this.scene.journalSystem) {
this._firstHarvest = true;
this.scene.time.delayedCall(500, () => {
this.scene.journalSystem.unlockEntry('first_harvest');
});
}
} }
// ───────────────────────────────────────────────────────────────────────── // ─────────────────────────────────────────────────────────────────────────

View File

@@ -29,17 +29,17 @@ export default class InventorySystem {
this.items = {}; this.items = {};
// Format: { 'wheat_seed': { name, icon, count, type, tool/seed } } // Format: { 'wheat_seed': { name, icon, count, type, tool/seed } }
// Hotbar = 7 slotov, vsak kaže na item key ali null // Hotbar = 7 slotov, vsak kaže na item key ali null (začenjamo prazni!)
this.hotbar = [ this.hotbar = [
'hoe', null,
'watering_can', null,
'wheat_seed', null,
'carrot_seed', null,
null, null,
null, null,
null, null,
]; ];
this.activeSlot = 0; // Slot 1 = Motika this.activeSlot = 0; // Slot 1
// Item definicije // Item definicije
this.ITEM_DEFS = { this.ITEM_DEFS = {
@@ -61,6 +61,15 @@ export default class InventorySystem {
count: 1, count: 1,
desc: '[E] Zalij rastlino', desc: '[E] Zalij rastlino',
}, },
axe: {
name: 'Sekira',
icon: '🪓',
type: 'tool',
tool: 'axe',
stackable: false,
count: 1,
desc: '[M1] Podri drevo',
},
wheat_seed: { wheat_seed: {
name: 'Pšenica', name: 'Pšenica',
icon: '🌾', icon: '🌾',
@@ -88,8 +97,9 @@ export default class InventorySystem {
seed: 'cannabis', seed: 'cannabis',
stackable: true, stackable: true,
count: 0, count: 0,
desc: '7 dni • 200g/harvest', desc: '[E] Posadi na preorano',
color: '#88cc44', color: '#88cc44',
value: 50,
}, },
// Harvested items // Harvested items
wheat: { wheat: {
@@ -116,6 +126,14 @@ export default class InventorySystem {
count: 0, count: 0,
value: 200, value: 200,
}, },
wood: {
name: 'Les',
icon: '🪵',
type: 'resource',
stackable: true,
count: 0,
value: 10,
},
}; };
// Inicializiraj items iz ITEM_DEFS (samo tiste s count > 0) // Inicializiraj items iz ITEM_DEFS (samo tiste s count > 0)
@@ -169,9 +187,9 @@ export default class InventorySystem {
} }
this.items[key].count += count; this.items[key].count += count;
// Dodaj v hotbar če je prazen slot + seme/resource // Dodaj v hotbar če je prazen slot in item še ni v hotbaru
const def = this.ITEM_DEFS[key]; const def = this.ITEM_DEFS[key];
if (def && def.stackable && !this.hotbar.includes(key)) { if (def && !this.hotbar.includes(key)) {
const emptySlot = this.hotbar.findIndex(s => s === null); const emptySlot = this.hotbar.findIndex(s => s === null);
if (emptySlot !== -1) this.hotbar[emptySlot] = key; if (emptySlot !== -1) this.hotbar[emptySlot] = key;
} }
@@ -186,6 +204,23 @@ export default class InventorySystem {
console.log(`💰 +${amount}g (skupaj: ${this.gold}g)`); console.log(`💰 +${amount}g (skupaj: ${this.gold}g)`);
} }
deductItem(key, amount) {
if (!this.items[key] || this.items[key].count < amount) return false;
this.items[key].count -= amount;
// Če je stackable in gre na 0, odstrani tudi iz hotbara
if (this.items[key].count <= 0 && this.items[key].stackable) {
const index = this.hotbar.indexOf(key);
if (index !== -1) {
this.hotbar[index] = null;
}
}
this._notifyUI();
return true;
}
// ───────────────────────────────────────────────────────────────────────── // ─────────────────────────────────────────────────────────────────────────
// PORABI ITEM (semena pri sajenju) // PORABI ITEM (semena pri sajenju)
// ───────────────────────────────────────────────────────────────────────── // ─────────────────────────────────────────────────────────────────────────

View File

@@ -0,0 +1,334 @@
/**
* ============================================================
* JournalSystem.js — Kaijev Dnevnik (Nova Farma)
* ============================================================
* [J] = odpri / zapri dnevnik
* Entries se odklenejo ob napredku (ob klicu unlockEntry(id))
* Stil: Temni gotski dnevnik z rumenimi robovi
* ============================================================
*/
export default class JournalSystem {
/**
* @param {Phaser.Scene} scene - GrassSceneClean instanca
*/
constructor(scene) {
this.scene = scene;
this.isOpen = false;
this.container = null;
this.currentPage = 0;
// ── Vse Vnosi (se odklenejo z unlockEntry) ──────────────────────
this.entries = [
{
id: 'start',
title: 'Dan 1 — Začetek',
text: [
'Prebudil sem se na otoku.',
'Nič se ne spomnim. Samo glas v glavi:',
'"Preživi. Posadi. Gradi."',
'',
'Poleg mene je bila skrinja. V njej orodja.',
'Začenjam.',
],
icon: '📖',
unlocked: true, // Startni vnos je vedno aktiven
},
{
id: 'first_tree',
title: 'Les z Gozda',
text: [
'Sekira se je dobro obnesla.',
'Drevo je padlo, les se je zvril na tla.',
'S tem lesom bom zgradil prve barikade.',
'',
'Noč se bliža. Slutim, da bo grdo.',
],
icon: '🪓',
unlocked: false,
},
{
id: 'first_plant',
title: 'Prve Sadike',
text: [
'Zemlja je rjava in mokra.',
'Prekopal sem jo z motiko, posadil pšenico.',
'Vsako jutro jo moram zaliti.',
'',
'Upam, da bo rasla hitro. Hrane je vedno manj.',
],
icon: '🌱',
unlocked: false,
},
{
id: 'first_night',
title: 'Prva Noč',
text: [
'Slišim jih.',
'Ropot v temi, stokanje. Hodijo počasi.',
'',
'Barikade so zdržale to noč.',
'A koliko noči jih je še pred mano?',
],
icon: '🌙',
unlocked: false,
},
{
id: 'first_harvest',
title: 'Žetev',
text: [
'Pšenica je zrasla!',
'Tri klasje. Malo, a dovolj za začetek.',
'',
'Zemlja tukaj je rodovitna kljub vsemu.',
'Morda je to kraj, kjer se da preživeti.',
],
icon: '🌾',
unlocked: false,
},
{
id: 'mystery',
title: '???',
text: [
'???',
],
icon: '❓',
unlocked: false,
},
];
// ── UI Setup ──────────────────────────────────────────────────────
this._buildUI();
// ── Tipka [J] ─────────────────────────────────────────────────────
this.keyJ = scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.J);
this.keyJ.on('down', this.toggle, this);
// ── Zapri z [Esc] ─────────────────────────────────────────────────
scene.input.keyboard.on('keydown-ESC', () => {
if (this.isOpen) this.close();
});
console.log('📖 JournalSystem initialized — [J] za dnevnik');
}
// ─────────────────────────────────────────────────────────────────────
// BUILD UI
// ─────────────────────────────────────────────────────────────────────
_buildUI() {
const { width, height } = this.scene.cameras.main;
const W = Math.min(660, width - 60);
const H = Math.min(460, height - 60);
const cx = width / 2;
const cy = height / 2;
// ── Wrapper (ScrollFactor 0 = fiksno na ekranu) ──
this.container = this.scene.add.container(cx, cy).setScrollFactor(0).setDepth(25000).setVisible(false);
// Temni tančati overlay
this.overlay = this.scene.add.rectangle(0, 0, width * 2, height * 2, 0x000000, 0.7)
.setScrollFactor(0).setDepth(24999).setVisible(false);
// ── Ozadje dnevnika (pergament) ──
const bg = this.scene.add.graphics();
// Senčni okvir
bg.fillStyle(0x1a0f00, 1);
bg.fillRoundedRect(-W / 2 + 5, -H / 2 + 5, W, H, 12);
// Pergament
bg.fillStyle(0x2a1a08, 1);
bg.fillRoundedRect(-W / 2, -H / 2, W, H, 10);
// Zlat rob
bg.lineStyle(3, 0xc8a040, 1);
bg.strokeRoundedRect(-W / 2, -H / 2, W, H, 10);
// Notranji rob
bg.lineStyle(1, 0x7a5020, 0.6);
bg.strokeRoundedRect(-W / 2 + 8, -H / 2 + 8, W - 16, H - 16, 8);
this.container.add(bg);
// ── Naslov ──
const title = this.scene.add.text(0, -H / 2 + 28, '📖 Kaijev Dnevnik', {
fontFamily: '"Cinzel", "Georgia", serif',
fontSize: '22px',
color: '#f0c060',
stroke: '#3a1a00',
strokeThickness: 4,
}).setOrigin(0.5, 0.5);
this.container.add(title);
// ── Razdelilna črta pod naslovom ──
const divider = this.scene.add.graphics();
divider.lineStyle(1, 0xc8a040, 0.6);
divider.lineBetween(-W / 2 + 20, -H / 2 + 50, W / 2 - 20, -H / 2 + 50);
this.container.add(divider);
// ── Leva stran: seznam vnosov ──
const listX = -W / 2 + 20;
this.entryButtons = [];
this._entryListY0 = -H / 2 + 65;
this._listStartX = listX;
this._panelW = W;
this._panelH = H;
// Placeholder za vnose (izgradujemo dinamično v refresh)
this._entryListContainer = this.scene.add.container(0, 0);
this.container.add(this._entryListContainer);
// ── Desna stran: vsebina ──
this._contentX = -W / 2 + W * 0.42;
this._contentY = -H / 2 + 65;
this._contentW = W * 0.57;
this._contentH = H - 90;
// Ločevalna črta med levo in desno
const div2 = this.scene.add.graphics();
div2.lineStyle(1, 0x7a5020, 0.5);
div2.lineBetween(-W / 2 + W * 0.41, -H / 2 + 55, -W / 2 + W * 0.41, H / 2 - 15);
this.container.add(div2);
// Vsebinski tekst (začasni, bo refreshan)
this._contentTitle = this.scene.add.text(this._contentX + 8, this._contentY + 10, '', {
fontFamily: '"Georgia", serif',
fontSize: '16px',
color: '#f0c060',
wordWrap: { width: this._contentW - 20 },
}).setOrigin(0, 0);
this.container.add(this._contentTitle);
this._contentBody = this.scene.add.text(this._contentX + 8, this._contentY + 40, '', {
fontFamily: '"Georgia", serif',
fontSize: '13px',
color: '#d4b872',
wordWrap: { width: this._contentW - 20 },
lineSpacing: 5,
}).setOrigin(0, 0);
this.container.add(this._contentBody);
// ── Gumb ZAPRI ──
const closeBtn = this.scene.add.text(W / 2 - 15, -H / 2 + 15, '✕', {
fontFamily: 'Arial',
fontSize: '18px',
color: '#c87050',
}).setOrigin(0.5, 0.5).setInteractive({ useHandCursor: true });
closeBtn.on('pointerdown', this.close, this);
closeBtn.on('pointerover', () => closeBtn.setColor('#ff7744'));
closeBtn.on('pointerout', () => closeBtn.setColor('#c87050'));
this.container.add(closeBtn);
// ── Navodila (spodaj) ──
const hint = this.scene.add.text(0, H / 2 - 15, '[J] Zapri | ←→ Strani', {
fontFamily: 'Arial',
fontSize: '11px',
color: '#7a5030',
}).setOrigin(0.5, 1);
this.container.add(hint);
}
// ─────────────────────────────────────────────────────────────────────
// REFRESH: Posodobi seznam vnosov in vsebino
// ─────────────────────────────────────────────────────────────────────
_refresh() {
// Počisti stari seznam
this._entryListContainer.removeAll(true);
const unlocked = this.entries.filter(e => e.unlocked);
this.currentPage = Math.max(0, Math.min(this.currentPage, unlocked.length - 1));
unlocked.forEach((entry, i) => {
const yOff = this._entryListY0 + i * 38;
const isActive = i === this.currentPage;
// Ozadje za aktivni vnos
if (isActive) {
const hl = this.scene.add.graphics();
hl.fillStyle(0x3a2010, 1);
hl.fillRoundedRect(this._listStartX, yOff - 14, this._panelW * 0.39, 32, 6);
hl.lineStyle(1, 0xc8a040, 0.7);
hl.strokeRoundedRect(this._listStartX, yOff - 14, this._panelW * 0.39, 32, 6);
this._entryListContainer.add(hl);
}
const row = this.scene.add.text(this._listStartX + 10, yOff, `${entry.icon} ${entry.title}`, {
fontFamily: '"Georgia", serif',
fontSize: '13px',
color: isActive ? '#f0c060' : '#a08040',
}).setOrigin(0, 0.5).setInteractive({ useHandCursor: true });
row.on('pointerdown', () => {
this.currentPage = i;
this._refresh();
});
row.on('pointerover', () => row.setColor('#ffe080'));
row.on('pointerout', () => row.setColor(isActive ? '#f0c060' : '#a08040'));
this._entryListContainer.add(row);
});
// Prikaži vsebino aktivnega vnosa
const active = unlocked[this.currentPage];
if (active) {
this._contentTitle.setText(`${active.icon} ${active.title}`);
this._contentBody.setText(active.text.join('\n'));
} else {
this._contentTitle.setText('');
this._contentBody.setText('');
}
}
// ─────────────────────────────────────────────────────────────────────
// TOGGLE / OPEN / CLOSE
// ─────────────────────────────────────────────────────────────────────
toggle() {
if (this.isOpen) {
this.close();
} else {
this.open();
}
}
open() {
this.isOpen = true;
this._refresh();
this.container.setVisible(true);
this.overlay.setVisible(true);
// Pojavi se animacija
this.container.setScale(0.85);
this.container.setAlpha(0);
this.scene.tweens.add({
targets: this.container,
scaleX: 1, scaleY: 1, alpha: 1,
duration: 220,
ease: 'Back.easeOut',
});
}
close() {
this.scene.tweens.add({
targets: this.container,
scaleX: 0.9, scaleY: 0.9, alpha: 0,
duration: 150,
ease: 'Sine.easeIn',
onComplete: () => {
this.container.setVisible(false);
this.overlay.setVisible(false);
this.isOpen = false;
}
});
}
// ─────────────────────────────────────────────────────────────────────
// UNLOCK ENTRY (kliči od drugod: this.journalSystem.unlockEntry('first_tree'))
// ─────────────────────────────────────────────────────────────────────
unlockEntry(id) {
const entry = this.entries.find(e => e.id === id);
if (!entry || entry.unlocked) return;
entry.unlocked = true;
console.log(`📖 Dnevnik odklenjen: "${entry.title}"`);
// Toast obvestilo
if (this.scene.inventorySystem) {
this.scene.inventorySystem._showMsg(`📖 Nov vnos: "${entry.title}"`, '#f0c060');
}
}
}

View File

@@ -0,0 +1,137 @@
/**
* TwinBondSystem.js
*
* Sistem, ki občasno pošlje srhljiv telepatski signal (spomin/klic) od Ane.
* Ekran naključno utripne vijolično in se izpiše skrit tekst v glavi Kaja.
*/
export default class TwinBondSystem {
constructor(scene) {
this.scene = scene;
this.isActive = false;
// Overlay za vijoličen blisk
this.flashOverlay = scene.add.graphics()
.setDepth(9999)
.setScrollFactor(0);
// Tekst na sredini zaslona
const cx = scene.cameras.main.width / 2;
const cy = scene.cameras.main.height / 2;
this.messageText = scene.add.text(cx, cy - 100, '', {
fontFamily: 'Courier New',
fontSize: '42px',
color: '#ff99ff',
stroke: '#2b002b',
strokeThickness: 8,
align: 'center',
fontStyle: 'bold'
})
.setOrigin(0.5, 0.5)
.setDepth(10000)
.setScrollFactor(0)
.setAlpha(0);
// Nabor Aninih sporočil
this.messages = [
"K a i . . . n a j d i m e . . .",
"T u k a j d o l j e t e m a . . .",
"N e o b u p a j . . . T w i n B o n d d e l u j e .",
"K a i . . . b o l i m e . . .",
"P a z i s e n o č i . . ."
];
// Tipka za testiranje (tipka T)
this.keyT = scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.T);
}
update(time, delta) {
// Testni sprožilec
if (Phaser.Input.Keyboard.JustDown(this.keyT) && !this.isActive) {
this.triggerBond();
}
// Naključno sprožanje (zelo majhna možnost ob igranju)
// 1 na 10,000 frejmov (~ vsake 3-5 minut)
if (Math.random() < 0.0001 && !this.isActive) {
// Samo če je zunaj spanja
this.triggerBond();
}
}
triggerBond() {
this.isActive = true;
// Zaustavi Kaja za kratek čas (šok)
if (this.scene.kai) {
this.scene.kai.canMove = false;
this.scene.kai.setVelocity(0, 0);
}
// 1. Zvok (Web Audio API srhljiv glitch freq)
this._playGlitchSound();
// 2. Narisemo purple flash, ki pokrije zaslon
const W = this.scene.cameras.main.width;
const H = this.scene.cameras.main.height;
this.flashOverlay.clear();
this.flashOverlay.fillStyle(0x3a0055, 0.85); // Temno vijolična
this.flashOverlay.fillRect(0, 0, W, H);
// Skrijemo čez 0.15s
this.scene.time.delayedCall(150, () => {
this.flashOverlay.clear();
});
// 3. Izberi random text in naredi typewriter
const text = Phaser.Utils.Array.GetRandom(this.messages);
this.messageText.setText('');
this.messageText.setAlpha(1);
let index = 0;
const typeTimer = this.scene.time.addEvent({
delay: 80,
repeat: text.length - 1,
callback: () => {
this.messageText.setText(text.substring(0, index + 1));
index++;
}
});
// 4. Fade out text in enable movement po 3 sekundah
this.scene.time.delayedCall(3000, () => {
this.scene.tweens.add({
targets: this.messageText,
alpha: 0,
duration: 1000,
onComplete: () => {
this.isActive = false;
if (this.scene.kai) {
this.scene.kai.canMove = true;
}
}
});
});
}
_playGlitchSound() {
if (!this.scene.sound.context) return;
const ctx = this.scene.sound.context;
const osc = ctx.createOscillator();
const gain = ctx.createGain();
// Srhljiv nizek zvok s frekvenčno modulacijo
osc.type = 'sawtooth';
osc.frequency.setValueAtTime(120, ctx.currentTime);
osc.frequency.linearRampToValueAtTime(40, ctx.currentTime + 1.5);
gain.gain.setValueAtTime(0.3, ctx.currentTime);
gain.gain.exponentialRampToValueAtTime(0.01, ctx.currentTime + 1.5);
osc.connect(gain);
gain.connect(ctx.destination);
osc.start();
osc.stop(ctx.currentTime + 1.5);
}
}

View File

@@ -27,12 +27,13 @@ export default class ZombieSystem {
this.islandH = options.islandH || 6400; this.islandH = options.islandH || 6400;
// Zombi parametri // Zombi parametri
this.maxZombies = 3; this.maxZombies = 2; // Začne z 2, raste po dnevih
this.spawnAtNight = true; this.spawnAtNight = true;
this.zombieSpeed = 35; // px/s — počasi šambers this.zombieSpeed = 35; // px/s — počasi šambers
this.detectionRange = 600; // Kai zazna zombija → prikaže opozorilo this.detectionRange = 600; // Kai zazna zombija → prikaže opozorilo
this.alfaRange = 120; // Razdalja za Alfa Moč [SPACE] this.alfaRange = 120; // Razdalja za Alfa Moč [SPACE]
this.tamingTime = 2000; // ms za udomačitev (drži [SPACE]) this.tamingTime = 2000; // ms za udomačitev (drži [SPACE])
this._nightCount = 0; // Koliko noči je minilo
// Aktivni zombiji // Aktivni zombiji
this.zombies = []; this.zombies = [];
@@ -94,8 +95,18 @@ export default class ZombieSystem {
// ───────────────────────────────────────────────────────────────────────── // ─────────────────────────────────────────────────────────────────────────
startNightSpawning() { startNightSpawning() {
if (this.spawnTimer) return; if (this.spawnTimer) return;
this._nightCount++;
// Stopnjevanje tezje po vsaki noci
this.maxZombies = Math.min(2 + this._nightCount, 8);
const spawnDelay = Math.max(6000, 14000 - this._nightCount * 1000);
this.zombieSpeed = Math.min(35 + this._nightCount * 3, 65);
console.log(`🌙 Noč ${this._nightCount}: max=${this.maxZombies}, delay=${spawnDelay}ms, speed=${this.zombieSpeed}px/s`);
this.spawnTimer = this.scene.time.addEvent({ this.spawnTimer = this.scene.time.addEvent({
delay: 12000, // Vsakih 12s poskusi spawnat (ponoči) delay: spawnDelay,
loop: true, loop: true,
callback: () => { callback: () => {
if (this.zombies.length < this.maxZombies) { if (this.zombies.length < this.maxZombies) {
@@ -103,10 +114,19 @@ export default class ZombieSystem {
} }
} }
}); });
// Takoj spawni prvega // Takoj spawni prvega
this.scene.time.delayedCall(2000, () => { this.scene.time.delayedCall(2000, () => {
if (this.zombies.length < this.maxZombies) this.spawnZombie(); if (this.zombies.length < this.maxZombies) this.spawnZombie();
}); });
// Journal unlock ob prvi noči
if (this._nightCount === 1 && this.scene.journalSystem) {
this.scene.time.delayedCall(3000, () => {
this.scene.journalSystem.unlockEntry('first_night');
});
}
console.log('🌙 Zombiji se začnejo spawnat!'); console.log('🌙 Zombiji se začnejo spawnat!');
} }