330 lines
13 KiB
JavaScript
330 lines
13 KiB
JavaScript
/**
|
|
* ============================================================
|
|
* 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 = [];
|
|
}
|
|
}
|