🔥 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:
329
nova farma TRAE/src/systems/WaterSystem.js
Normal file
329
nova farma TRAE/src/systems/WaterSystem.js
Normal 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 = [];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user