🔥 FAZE SPRINT 1-3 COMPLETION: DayNight, Inventory, Core Farming System, Mrtvi Zombiji (ShamblerAI & Alfa Moć Taming), Sredili assets & daily UI

This commit is contained in:
2026-03-03 17:46:41 +01:00
parent 35153eeacf
commit 188bd769cf
49 changed files with 5054 additions and 286 deletions

View File

@@ -0,0 +1,329 @@
/**
* ============================================================
* WaterSystem.js — Nova Farma (DEMO)
* ============================================================
* Voda je omejena v Demu — edini vir = Rain Catcher.
*
* Logika:
* - Rain Catcher zbira vodo med dežjem (triggerRainfall)
* - Max kapaciteta = 20 L na Rain Catcher
* - Zalivanje porabi 1 L na crop
* - Kai dobi "vedro" (watering_can) s 5L kapaciteto
* - [E] blizu Rain Catcher → Napolni vedro iz Rain Catcher-ja
* - UIScene prikaže: 💧 vedro: 3/5 | 🪣 RC: 12/20
* ============================================================
*/
export default class WaterSystem {
/**
* @param {Phaser.Scene} scene
* @param {object} options — { islandX, islandY, tileSize }
*/
constructor(scene, options = {}) {
this.scene = scene;
this.tileSize = options.tileSize || 128;
// === Vedro (Watering Can) ===
this.canCurrent = 3; // Začne s 3L
this.canMax = 5; // Max 5L v vedru
this.canPerCrop = 1; // 1L na zalivanje
// === Rain Catcher objekti ===
this.rainCatchers = []; // { x, y, sprite, current, max, label }
// === Highlights ===
this.highlightGfx = scene.add.graphics().setDepth(9001);
// === Input [F] za napolnit vedro ===
this.keyF = scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.F);
// === Interact razdalja ===
this.interactRange = 100;
// === Dežjni multiplier ===
this.rainCollectRate = 4; // L na dežen dan na RC
console.log('💧 WaterSystem initialized');
}
// ─────────────────────────────────────────────────────────────────────────
// PRELOAD
// ─────────────────────────────────────────────────────────────────────────
static preload(scene) {
scene.load.image('rain_catcher', 'DEMO_FAZA1/Structures/rain_catcher.png');
}
// ─────────────────────────────────────────────────────────────────────────
// PLACE RAIN CATCHER (kliče se ob postavitvi zgradbe ali ob init)
// ─────────────────────────────────────────────────────────────────────────
placeRainCatcher(x, y) {
const targetH = 100; // px višina v svetu
const texture = this.scene.textures.get('rain_catcher');
const srcH = texture?.getSourceImage()?.height || 512;
const scale = targetH / srcH;
const sprite = this.scene.add.image(x, y, 'rain_catcher')
.setScale(scale)
.setOrigin(0.5, 1.0)
.setDepth(y)
.setInteractive();
// Label nad RC
const label = this.scene.add.text(x, y - targetH - 8, '🪣 0/20L', {
fontFamily: 'Arial Black',
fontSize: '12px',
color: '#44aaff',
stroke: '#000000',
strokeThickness: 3,
}).setOrigin(0.5, 1.0).setDepth(y + 1);
const rc = {
x, y,
sprite,
label,
current: 0,
max: 20,
};
this.rainCatchers.push(rc);
this._updateLabel(rc);
// Pop-in animacija
sprite.setAlpha(0).setScale(scale * 0.1);
this.scene.tweens.add({
targets: sprite,
alpha: 1,
scaleX: scale,
scaleY: scale,
duration: 500,
ease: 'Back.out',
});
console.log(`🪣 Rain Catcher postavljen @ ${x}, ${y}`);
return rc;
}
// ─────────────────────────────────────────────────────────────────────────
// UPDATE — kliči iz scene.update()
// ─────────────────────────────────────────────────────────────────────────
update(kai) {
this.highlightGfx.clear();
const nearRC = this._getRCNear(kai);
if (nearRC) {
// Highlight RC
this.highlightGfx.lineStyle(2, 0x44aaff, 0.9);
this.highlightGfx.strokeCircle(nearRC.x, nearRC.y - 50, 60);
// Tooltip
this._showRCTooltip(nearRC);
} else {
this._hideRCTooltip();
}
// [F] — Napolni vedro
if (Phaser.Input.Keyboard.JustDown(this.keyF)) {
if (nearRC) {
this._fillCan(nearRC);
} else {
this._showFloatingTextAt(kai.x, kai.y - 40, '🪣 Bliže Rain Catcher!', '#aaaaff', 14);
}
}
}
// ─────────────────────────────────────────────────────────────────────────
// NAPOLNI VEDRO iz Rain Catcher-ja
// ─────────────────────────────────────────────────────────────────────────
_fillCan(rc) {
if (rc.current <= 0) {
this._showFloatingTextAt(rc.x, rc.y - 110, '❌ RC je prazen!', '#ff6666');
return;
}
if (this.canCurrent >= this.canMax) {
this._showFloatingTextAt(rc.x, rc.y - 110, '✅ Vedro je polno!', '#44ffcc');
return;
}
const needed = this.canMax - this.canCurrent;
const take = Math.min(needed, rc.current);
rc.current -= take;
this.canCurrent += take;
this._updateLabel(rc);
this.scene.events.emit('waterChanged', {
can: this.canCurrent,
canMax: this.canMax,
});
// Animacija — vodni curek (modri krogci padajo v vedro)
this._fillAnimation(rc.x, rc.y - 30);
this._showFloatingTextAt(rc.x, rc.y - 110, `💧 +${take}L (vedro: ${this.canCurrent}/${this.canMax}L)`, '#44aaff', 16);
console.log(`💧 Napolnjeno: +${take}L. Vedro: ${this.canCurrent}/${this.canMax}L. RC: ${rc.current}/${rc.max}L`);
}
// ─────────────────────────────────────────────────────────────────────────
// PORABI VODO ZA ZALIVANJE (kliče FarmingSystem)
// ─────────────────────────────────────────────────────────────────────────
useWater(amount = 1) {
if (this.canCurrent < amount) return false; // Ni dovolj vode!
this.canCurrent -= amount;
this.scene.events.emit('waterChanged', {
can: this.canCurrent,
canMax: this.canMax,
});
return true;
}
hasWater(amount = 1) {
return this.canCurrent >= amount;
}
// ─────────────────────────────────────────────────────────────────────────
// DEŽNI TICK — kliči ob dežju (triggerRainfall v GrassScene)
// ─────────────────────────────────────────────────────────────────────────
onRain() {
let total = 0;
for (const rc of this.rainCatchers) {
const collected = Math.min(this.rainCollectRate, rc.max - rc.current);
rc.current += collected;
total += collected;
this._updateLabel(rc);
// Ripple animacija na RC
this._rainRipple(rc.x, rc.y - 40);
}
if (total > 0) {
console.log(`🌧️ Dež zbral ${total}L v Rain Catcher-jih`);
}
this.scene.events.emit('waterChanged', {
can: this.canCurrent,
canMax: this.canMax,
});
}
// ─────────────────────────────────────────────────────────────────────────
// POMOŽNE METODE
// ─────────────────────────────────────────────────────────────────────────
_getRCNear(kai) {
let closest = null;
let minDist = this.interactRange;
for (const rc of this.rainCatchers) {
const d = Phaser.Math.Distance.Between(kai.x, kai.y, rc.x, rc.y);
if (d < minDist) { minDist = d; closest = rc; }
}
return closest;
}
_updateLabel(rc) {
const pct = rc.current / rc.max;
const color = pct > 0.5 ? '#44aaff' : pct > 0.2 ? '#ffaa00' : '#ff4444';
rc.label
.setText(`🪣 ${rc.current}/${rc.max}L`)
.setColor(color);
}
_showRCTooltip(rc) {
if (!this._rcTooltip) {
this._rcTooltip = this.scene.add.text(0, 0, '', {
fontFamily: 'Arial Black',
fontSize: '13px',
color: '#ffffff',
stroke: '#000000',
strokeThickness: 4,
backgroundColor: '#00000099',
padding: { x: 8, y: 4 },
}).setDepth(9999).setScrollFactor(1);
}
const label = this.canCurrent >= this.canMax
? `✅ Vedro polno (${this.canCurrent}L)`
: `[F] Napolni vedro 💧${this.canCurrent}/${this.canMax}L`;
this._rcTooltip
.setText(label)
.setAlpha(1)
.setPosition(rc.x - this._rcTooltip.width / 2, rc.y - 115);
}
_hideRCTooltip() {
if (this._rcTooltip) this._rcTooltip.setAlpha(0);
}
_fillAnimation(x, y) {
// Padajoče kapljice
for (let i = 0; i < 5; i++) {
const drop = this.scene.add.graphics().setDepth(9998);
const ox = x + Phaser.Math.Between(-15, 15);
drop.fillStyle(0x44aaff, 0.8);
drop.fillEllipse(ox, y, 4, 8);
this.scene.tweens.add({
targets: drop,
y: y + 30 + Phaser.Math.Between(0, 20),
alpha: 0,
delay: i * 60,
duration: 300,
ease: 'Quad.in',
onComplete: () => drop.destroy(),
});
}
}
_rainRipple(x, y) {
const gfx = this.scene.add.graphics().setDepth(9997);
let r = 2, alpha = 0.6;
const ev = this.scene.time.addEvent({
delay: 50,
repeat: 5,
callback: () => {
gfx.clear();
gfx.lineStyle(1, 0x44aaff, alpha);
gfx.strokeEllipse(x, y, r * 2, r);
r += 4; alpha -= 0.1;
if (r > 25) { gfx.destroy(); ev.remove(); }
}
});
}
_showFloatingTextAt(x, y, msg, color = '#ffffff', fontSize = 18) {
const txt = this.scene.add.text(x, y, msg, {
fontFamily: 'Arial Black',
fontSize: `${fontSize}px`,
color,
stroke: '#000000',
strokeThickness: 4,
}).setOrigin(0.5).setDepth(9998);
this.scene.tweens.add({
targets: txt,
y: y - 50,
alpha: { from: 1, to: 0 },
duration: 1600,
ease: 'Cubic.out',
onComplete: () => txt.destroy(),
});
}
// ─────────────────────────────────────────────────────────────────────────
// PUBLIC API
// ─────────────────────────────────────────────────────────────────────────
/** Vrni stanje za save */
getState() {
return {
can: this.canCurrent,
catchers: this.rainCatchers.map(rc => ({ x: rc.x, y: rc.y, current: rc.current })),
};
}
destroy() {
this.highlightGfx?.destroy();
this._rcTooltip?.destroy();
this.rainCatchers.forEach(rc => {
rc.sprite?.destroy();
rc.label?.destroy();
});
this.rainCatchers = [];
}
}