diff --git a/nova farma TRAE/.DS_Store b/nova farma TRAE/.DS_Store index 6e62ae849..bcfa16eff 100644 Binary files a/nova farma TRAE/.DS_Store and b/nova farma TRAE/.DS_Store differ diff --git a/nova farma TRAE/assets/.DS_Store b/nova farma TRAE/assets/.DS_Store index 7d8f6a033..8f188d7d6 100644 Binary files a/nova farma TRAE/assets/.DS_Store and b/nova farma TRAE/assets/.DS_Store differ diff --git a/nova farma TRAE/assets/DEMO_FAZA1/.DS_Store b/nova farma TRAE/assets/DEMO_FAZA1/.DS_Store index 734a5db0e..2bf04cde7 100644 Binary files a/nova farma TRAE/assets/DEMO_FAZA1/.DS_Store and b/nova farma TRAE/assets/DEMO_FAZA1/.DS_Store differ diff --git a/nova farma TRAE/assets/DEMO_FAZA1/Characters/zombie_builder_idle.png b/nova farma TRAE/assets/DEMO_FAZA1/Characters/zombie_builder_idle.png new file mode 100644 index 000000000..dda15d722 Binary files /dev/null and b/nova farma TRAE/assets/DEMO_FAZA1/Characters/zombie_builder_idle.png differ diff --git a/nova farma TRAE/assets/DEMO_FAZA1/Characters/zombie_chef_idle.png b/nova farma TRAE/assets/DEMO_FAZA1/Characters/zombie_chef_idle.png new file mode 100644 index 000000000..7796b389d Binary files /dev/null and b/nova farma TRAE/assets/DEMO_FAZA1/Characters/zombie_chef_idle.png differ diff --git a/nova farma TRAE/assets/DEMO_FAZA1/Characters/zombie_farmer_idle.png b/nova farma TRAE/assets/DEMO_FAZA1/Characters/zombie_farmer_idle.png new file mode 100644 index 000000000..8f59c2077 Binary files /dev/null and b/nova farma TRAE/assets/DEMO_FAZA1/Characters/zombie_farmer_idle.png differ diff --git a/nova farma TRAE/assets/DEMO_FAZA1/Characters/zombie_lumberjack_idle.png b/nova farma TRAE/assets/DEMO_FAZA1/Characters/zombie_lumberjack_idle.png new file mode 100644 index 000000000..87606bcd8 Binary files /dev/null and b/nova farma TRAE/assets/DEMO_FAZA1/Characters/zombie_lumberjack_idle.png differ diff --git a/nova farma TRAE/assets/DEMO_FAZA1/Characters/zombie_miner_idle.png b/nova farma TRAE/assets/DEMO_FAZA1/Characters/zombie_miner_idle.png new file mode 100644 index 000000000..c5d67af32 Binary files /dev/null and b/nova farma TRAE/assets/DEMO_FAZA1/Characters/zombie_miner_idle.png differ diff --git a/nova farma TRAE/assets/DEMO_FAZA1/Characters/zombie_porter_walk.png b/nova farma TRAE/assets/DEMO_FAZA1/Characters/zombie_porter_walk.png new file mode 100644 index 000000000..7c7bd18b9 Binary files /dev/null and b/nova farma TRAE/assets/DEMO_FAZA1/Characters/zombie_porter_walk.png differ diff --git a/nova farma TRAE/assets/DEMO_FAZA1/Characters/zombie_scout_idle.png b/nova farma TRAE/assets/DEMO_FAZA1/Characters/zombie_scout_idle.png new file mode 100644 index 000000000..60d027bfe Binary files /dev/null and b/nova farma TRAE/assets/DEMO_FAZA1/Characters/zombie_scout_idle.png differ diff --git a/nova farma TRAE/assets/DEMO_FAZA1/Characters/zombie_shambler_walk_sheet.png b/nova farma TRAE/assets/DEMO_FAZA1/Characters/zombie_shambler_walk_sheet.png new file mode 100644 index 000000000..553d2b836 Binary files /dev/null and b/nova farma TRAE/assets/DEMO_FAZA1/Characters/zombie_shambler_walk_sheet.png differ diff --git a/nova farma TRAE/assets/DEMO_FAZA1/Crops/crop_cannabis_sheet.png b/nova farma TRAE/assets/DEMO_FAZA1/Crops/crop_cannabis_sheet.png new file mode 100644 index 000000000..cd4d4c48f Binary files /dev/null and b/nova farma TRAE/assets/DEMO_FAZA1/Crops/crop_cannabis_sheet.png differ diff --git a/nova farma TRAE/assets/DEMO_FAZA1/Crops/crop_cannabis_stage0.png b/nova farma TRAE/assets/DEMO_FAZA1/Crops/crop_cannabis_stage0.png new file mode 100644 index 000000000..fce31cf2b Binary files /dev/null and b/nova farma TRAE/assets/DEMO_FAZA1/Crops/crop_cannabis_stage0.png differ diff --git a/nova farma TRAE/assets/DEMO_FAZA1/Crops/crop_cannabis_stage1.png b/nova farma TRAE/assets/DEMO_FAZA1/Crops/crop_cannabis_stage1.png new file mode 100644 index 000000000..c8e47d290 Binary files /dev/null and b/nova farma TRAE/assets/DEMO_FAZA1/Crops/crop_cannabis_stage1.png differ diff --git a/nova farma TRAE/assets/DEMO_FAZA1/Crops/crop_cannabis_stage2.png b/nova farma TRAE/assets/DEMO_FAZA1/Crops/crop_cannabis_stage2.png new file mode 100644 index 000000000..b9b1e8871 Binary files /dev/null and b/nova farma TRAE/assets/DEMO_FAZA1/Crops/crop_cannabis_stage2.png differ diff --git a/nova farma TRAE/assets/DEMO_FAZA1/Crops/crop_cannabis_stage3.png b/nova farma TRAE/assets/DEMO_FAZA1/Crops/crop_cannabis_stage3.png new file mode 100644 index 000000000..44526bfd0 Binary files /dev/null and b/nova farma TRAE/assets/DEMO_FAZA1/Crops/crop_cannabis_stage3.png differ diff --git a/nova farma TRAE/assets/DEMO_FAZA1/Crops/crop_cannabis_stage4.png b/nova farma TRAE/assets/DEMO_FAZA1/Crops/crop_cannabis_stage4.png new file mode 100644 index 000000000..0997bfafe Binary files /dev/null and b/nova farma TRAE/assets/DEMO_FAZA1/Crops/crop_cannabis_stage4.png differ diff --git a/nova farma TRAE/assets/DEMO_FAZA1/Crops/crop_cannabis_stage5.png b/nova farma TRAE/assets/DEMO_FAZA1/Crops/crop_cannabis_stage5.png new file mode 100644 index 000000000..00ccc4dd9 Binary files /dev/null and b/nova farma TRAE/assets/DEMO_FAZA1/Crops/crop_cannabis_stage5.png differ diff --git a/nova farma TRAE/assets/DEMO_FAZA1/Crops/crop_carrot_sheet.png b/nova farma TRAE/assets/DEMO_FAZA1/Crops/crop_carrot_sheet.png new file mode 100644 index 000000000..2e300c9bf Binary files /dev/null and b/nova farma TRAE/assets/DEMO_FAZA1/Crops/crop_carrot_sheet.png differ diff --git a/nova farma TRAE/assets/DEMO_FAZA1/Crops/crop_carrot_stage0.png b/nova farma TRAE/assets/DEMO_FAZA1/Crops/crop_carrot_stage0.png new file mode 100644 index 000000000..1d74936f4 Binary files /dev/null and b/nova farma TRAE/assets/DEMO_FAZA1/Crops/crop_carrot_stage0.png differ diff --git a/nova farma TRAE/assets/DEMO_FAZA1/Crops/crop_carrot_stage1.png b/nova farma TRAE/assets/DEMO_FAZA1/Crops/crop_carrot_stage1.png new file mode 100644 index 000000000..b7ba50def Binary files /dev/null and b/nova farma TRAE/assets/DEMO_FAZA1/Crops/crop_carrot_stage1.png differ diff --git a/nova farma TRAE/assets/DEMO_FAZA1/Crops/crop_carrot_stage2.png b/nova farma TRAE/assets/DEMO_FAZA1/Crops/crop_carrot_stage2.png new file mode 100644 index 000000000..dee27213c Binary files /dev/null and b/nova farma TRAE/assets/DEMO_FAZA1/Crops/crop_carrot_stage2.png differ diff --git a/nova farma TRAE/assets/DEMO_FAZA1/Crops/crop_carrot_stage3.png b/nova farma TRAE/assets/DEMO_FAZA1/Crops/crop_carrot_stage3.png new file mode 100644 index 000000000..f8ed1c65a Binary files /dev/null and b/nova farma TRAE/assets/DEMO_FAZA1/Crops/crop_carrot_stage3.png differ diff --git a/nova farma TRAE/assets/DEMO_FAZA1/Crops/crop_carrot_stage4.png b/nova farma TRAE/assets/DEMO_FAZA1/Crops/crop_carrot_stage4.png new file mode 100644 index 000000000..83e4135d6 Binary files /dev/null and b/nova farma TRAE/assets/DEMO_FAZA1/Crops/crop_carrot_stage4.png differ diff --git a/nova farma TRAE/assets/DEMO_FAZA1/Crops/crop_wheat_sheet.png b/nova farma TRAE/assets/DEMO_FAZA1/Crops/crop_wheat_sheet.png new file mode 100644 index 000000000..94f78ecd6 Binary files /dev/null and b/nova farma TRAE/assets/DEMO_FAZA1/Crops/crop_wheat_sheet.png differ diff --git a/nova farma TRAE/assets/DEMO_FAZA1/Crops/crop_wheat_stage0.png b/nova farma TRAE/assets/DEMO_FAZA1/Crops/crop_wheat_stage0.png new file mode 100644 index 000000000..85388def9 Binary files /dev/null and b/nova farma TRAE/assets/DEMO_FAZA1/Crops/crop_wheat_stage0.png differ diff --git a/nova farma TRAE/assets/DEMO_FAZA1/Crops/crop_wheat_stage1.png b/nova farma TRAE/assets/DEMO_FAZA1/Crops/crop_wheat_stage1.png new file mode 100644 index 000000000..592c57d3e Binary files /dev/null and b/nova farma TRAE/assets/DEMO_FAZA1/Crops/crop_wheat_stage1.png differ diff --git a/nova farma TRAE/assets/DEMO_FAZA1/Crops/crop_wheat_stage2.png b/nova farma TRAE/assets/DEMO_FAZA1/Crops/crop_wheat_stage2.png new file mode 100644 index 000000000..9c91975df Binary files /dev/null and b/nova farma TRAE/assets/DEMO_FAZA1/Crops/crop_wheat_stage2.png differ diff --git a/nova farma TRAE/assets/DEMO_FAZA1/Crops/crop_wheat_stage3.png b/nova farma TRAE/assets/DEMO_FAZA1/Crops/crop_wheat_stage3.png new file mode 100644 index 000000000..a11be8a78 Binary files /dev/null and b/nova farma TRAE/assets/DEMO_FAZA1/Crops/crop_wheat_stage3.png differ diff --git a/nova farma TRAE/assets/DEMO_FAZA1/Crops/tilled_soil.png b/nova farma TRAE/assets/DEMO_FAZA1/Crops/tilled_soil.png new file mode 100644 index 000000000..014427679 Binary files /dev/null and b/nova farma TRAE/assets/DEMO_FAZA1/Crops/tilled_soil.png differ diff --git a/nova farma TRAE/assets/DEMO_FAZA1/Structures/statue_200days.png b/nova farma TRAE/assets/DEMO_FAZA1/Structures/statue_200days.png new file mode 100755 index 000000000..bd8fbd969 Binary files /dev/null and b/nova farma TRAE/assets/DEMO_FAZA1/Structures/statue_200days.png differ diff --git a/nova farma TRAE/assets/DEMO_FAZA1/Trees/drevo_faza_2.png b/nova farma TRAE/assets/DEMO_FAZA1/Trees/drevo_faza_2.png old mode 100644 new mode 100755 index 36cdf8994..e8edbff71 Binary files a/nova farma TRAE/assets/DEMO_FAZA1/Trees/drevo_faza_2.png and b/nova farma TRAE/assets/DEMO_FAZA1/Trees/drevo_faza_2.png differ diff --git a/nova farma TRAE/assets/DEMO_FAZA1/Trees/drevo_veliko.png b/nova farma TRAE/assets/DEMO_FAZA1/Trees/drevo_veliko.png old mode 100644 new mode 100755 index c2dd21a04..f388f3118 Binary files a/nova farma TRAE/assets/DEMO_FAZA1/Trees/drevo_veliko.png and b/nova farma TRAE/assets/DEMO_FAZA1/Trees/drevo_veliko.png differ diff --git a/nova farma TRAE/assets/DEMO_FAZA1/UI/menu_background.png b/nova farma TRAE/assets/DEMO_FAZA1/UI/menu_background.png new file mode 100644 index 000000000..39f62141c Binary files /dev/null and b/nova farma TRAE/assets/DEMO_FAZA1/UI/menu_background.png differ diff --git a/nova farma TRAE/dokumentacija/DEVLOG_2026_03_03_SPRINTS_1_3.md b/nova farma TRAE/dokumentacija/DEVLOG_2026_03_03_SPRINTS_1_3.md new file mode 100644 index 000000000..5c45481a9 --- /dev/null +++ b/nova farma TRAE/dokumentacija/DEVLOG_2026_03_03_SPRINTS_1_3.md @@ -0,0 +1,42 @@ +# โฑ๏ธ DEVLOG IN DNEVNIK DELA +**Datum:** 3. Marec 2026 +**Zadeva:** Nova Farma Demo - Sprint 1, 2, 3 in Asseti (Zakljuฤek dneva) +**Status:** โœ… FAZA DEMO 65% COMPLETION + +## ๐Ÿ“ Povzetek danaลกnjega dela + +Danes smo opravili masiven premik naprej v razvoju "Nova Farma" DEMO verzije, pri ฤemer smo v celoti zakljuฤili prve tri sprinte od petih, ki so potrebni za izdajo Demo verzije igre. + +### ๐ŸŽฏ Kaj je bilo narejeno: + +**โœ… SPRINT 1 โ€” "Osnove Preลพivetja" (100% DONE)** +- Dodan `DayNightSystem.js` (ambientna svetloba, zvezde na noฤnem nebu). +- Implementirano spanje (tipka `[Z]` ob postelji/vreฤi za preskok noฤi do 06:00 zjutraj). +- ฤŒas in dnevi sedaj dejansko teฤejo in se avtomatsko sinhronizirajo z `UIScene`. +- Ustvarjen `InventorySystem.js` s funkcionalnim 7-slot hotbarom (tipke `[1]-[7]` ali koleลกฤek na miลกki). +- Dodeljeni zaฤetni starter itemi (Motika, Zalivalka, Semena: Pลกenica in Korenje). + +**โœ… SPRINT 2 โ€” "Kmetovanje" (100% DONE)** +- Refaktoriran `FarmingSystem.js` za uporabo Phaser Graphics za natanฤne 48x24px pikselske tilled soil (poorana tla) ploลกฤice brez popaฤenj. +- Urejeno sajenje semen iz inventarja. +- Urejen `WaterSystem.js` z zbiralnikom deลพevnice (Rain Catcher), kjer Kai zajema vodo (tipka `[F]`) in zaliva sode `[E]`. +- Implementirana vizualna metrika rasti in odvisnost rasti rastlin od ฤasa (`onNewDay`) in vode. +- Razreลกeni ERR_FILE_NOT_FOUND preloading manjki glede dreves in uveljavljen pixel-perfect z-index sistem rastlinstva. + +**โœ… SPRINT 3 โ€” "Zombi AI Sistem" (100% DONE)** +- Generiran nov `zombie_shambler_walk_sheet.png` preko AI v Dark Chibi Gothic stilu (4 smeri x 4 frames). +- Skripta je samodejno odstranila belo ozadje in pripravila sheet za engine. +- Skripta je uspeลกno skopirala ลพe obstojeฤe in narejene dobre zombije iz starega repozitorija v trenutnega (`assets/DEMO_FAZA1/Characters`). +- Narejen modul `ZombieSystem.js`. Zombiji sedaj prespawnajo ponoฤi na robovih otoka. +- Zombi AI logika urejena: Wandering (nakljuฤno), Chasing (zasleduje Kaia). +- Dodan sistem **Alfa Moฤ**: ko se igralec pribliลพa nezavestnemu zombiju in drลพi presledek (`[SPACE]`), vizualna "progress bar" koda (koordinate, taming bar) igralcu prepusti udomaฤenega zombija z zlatim vizualnim partikli efektom. + +### ๐Ÿ“ฆ Status Datotek in Sistemski Pregled: +Posodobili smo `STATUS_DEMO_FAZA1_2026_03_03.md`, ki sedaj kaลพe na uradnih 65% preboja k izidu samega dema. + +## ๐Ÿš€ Naslednji koraki (za naslednjo sejo): +- **Sprint 4 (Zgodba):** 20 polaroidnih story slik z AI, uvoz in vzpostavitev `IntroScene.js`. Dodatek "Amnesia" glitches na ekran ob zaฤetku. +- **Sprint 5 (Poliranje):** Generacija FX soundboarda - deลพ (web audio noise), stopinje in ambientni zvoki. Postavitev Demo menija in ureditve za Steam/Kickstarter predogled. +- **Dialog System:** Typewriter interakcija z NPC-jem (Gronk) za mali tutorial v igri. + +Dnevnik konฤan in sistem prepuลกฤen v roke git repozitorija. diff --git a/nova farma TRAE/dokumentacija/GEMINI_FAZE_KOMPLET_2026_03_03.md b/nova farma TRAE/dokumentacija/GEMINI_FAZE_KOMPLET_2026_03_03.md new file mode 100644 index 000000000..2fbbafefb --- /dev/null +++ b/nova farma TRAE/dokumentacija/GEMINI_FAZE_KOMPLET_2026_03_03.md @@ -0,0 +1,404 @@ +# ๐ŸŽฎ NOVA FARMA โ€” KOMPLETNA STRUKTURA FAZ +## Prompt za Gemini | Avtor: David Kotnik | 2026-03-03 + +--- + +## โšก KAJ JE IGRA (Hitri opis) + +**Nova Farma (Krvava ลฝetev)** je dark survival farming RPG. + +- **Protagonist:** Kai Markoviฤ‡, 14 let, Alpha Hybrid โ€” ugriznjen od zombija, a ni postal eden. Dobi moฤ kontrolirati zombije. +- **Cilj:** Preลพivi, zgradi kmetijo, obnovi civilizacijo, najdi ugrabljeno sestro Ano. +- **Svet:** Post-apokaliptiฤna Slovenija, leto 2084. Zaฤneลก na malem otoku, nato odkrivaลก celinski svet. +- **Styl:** 2.5D Dark Chibi Noir. Hand-drawn ilustracija (NI pixel art). Gothic dark barve. +- **Platform:** Desktop (Electron + Phaser 3) +- **Inspiracija:** Stardew Valley ร— The Last of Us ร— Don't Starve ร— My Time at Portia + +--- + +## ๐Ÿ—บ๏ธ STRUKTURA SVET + +``` +[DEMO] Kmetijski Otok (8ร—8 area) + โ†“ po 200 dneh +[FAZA 1] Celoten Otok odkrit (Fog of War se dvigne) + โ†“ po nakupu igre +[FAZA 2+] Celinska Mapa: 27 Razruลกenih Mest + 20 Biome-ov + โ†“ endgame +[FAZA 3+] ฤŒernobil, Ana reลกi, Finalni konec +``` + +**Kmetijski Otok** = tvoja stalna baza/dom, nikoli ga ne zapustiลก. Ampak v Fazi 2+ greลก na celino in obnavljaลก mesta. + +--- + +## ๐Ÿ“… KOMPLETNIH 10 FAZ โ€” TOฤŒNA VSEBINA + +--- + +### ๐Ÿ†“ DEMO โ€” "The Hook" (Brezplaฤno) + +**Cilj:** Preลพivi 200 dni โ†’ Achievement "Trmasti Preลพiveli" โ†’ Odkleni Fazo 1 + +**Svet:** Samo Farma (8ร—8 tilov) na kmetijskem otoku. Vse ostalo je Fog of War. + +**Vsebina:** +- **Bivanje:** Spalna Vreฤa โ†’ ล otor (Hiลกa zaklenjena) +- **Orodja:** Samo Lesena (Tier 1) +- **Rastline:** Pลกenica (3 dni), Korenje (4 dni), **Konoplja (7 dni โ€” GLAVNI ZASLUลฝEK!)** +- **Zombiji:** Max 3 Shamblerji. Samo sledijo, ne delajo. [SPACE] = Alfa Moฤ = Udomaฤiลก. +- **Voda:** VSA voda je strupena. Edini vir = Rain Catcher (lovilec deลพevnice) +- **Zgodba:** 20 Polaroid intro, Twin Bond signali od Ane (1ร—/dan, 10% chance), 3 Flashback spomini + +**Economy:** Konoplja = 200g/bud. Pลกenica = 15g, Korenje = 40g. Brez konoplje ne preลพiviลก. + +**Save file se prenese v polno igro!** + +--- + +### ๐Ÿ’ฐ FAZA 1 โ€” "Early Access: Odprtje Sveta" (Nakup igre) + +**Cilj:** Raziลกฤi celoten otok, upravljaj Zombi Workforce, popravi Ruลกevine + +**Svet:** CELOTEN OTOK odkrit (Fog of War se dvigne). Izvidnik Zombi gre v meglo in odkriva svet. + +**Vsebina:** + +**๐ŸงŸ Zombi Workforce (5 tipov):** +| Tip | Naloga | Max | +|-----|--------|-----| +| ๐Ÿ•ต๏ธ Scout (Izvidnik) | Gre v Fog, odkriva mapo, prinaลกa material | 1 | +| ๐ŸŒพ Farmer (Kmet) | Zaliva, ลพanje | 3 | +| ๐Ÿช“ Lumberjack (Gozdar) | Seka drevesa | 2 | +| โ›๏ธ Miner (Rudar) | Koplje rudo, Lv5+ najde ลฝelezo | 2 | +| ๐Ÿ“ฆ Porter (Nosaฤ) | Pobira Drop Boxe โ†’ v skrinje | 1 | + +**Drop Box sistem:** Zombiji ne nosijo v tvoje skrinje direktno. Odlagajo v Drop Box โ†’ ti pobereลก. + +**๐Ÿš๏ธ Ruลกevine (popravi jih!):** +- Garaลพa โ†’ Crafting hub (Motorka, Laksarca, Vodni Filter, Stiskalnica) +- Rastlinjak โ†’ Gobe (indoor), zimske rastline +- Hlev โ†’ Krave, Gronk, Susi + +**๐Ÿ  Bivanje:** Lepena Koฤa (Faza 1 MAX) + +**โ›๏ธ Rudnik:** Baker, Premog, Glina, ลฝelezo (samo Rudar Lv5+!) + +**๐ŸŒฑ Nove rastline:** 80+ vrst. Sonฤnice โ†’ Bio-olje. Konoplja full unlock. Gobe (Klet). + +**๐Ÿ„ Skrita Klet:** +- Vhod skrit (za omaro / pod preprogo) +- Grow Room: UV luฤi, Hidroponika, Police za Gobe +- The Lab: Predelava โ†’ Mind Expansion napitki + +**๐Ÿ” ลฝivali:** Kure (Kokoลกnjak), Krave (Hlev) โ€” dostava v ลกkatli + +**๐Ÿ‘ป Oltar:** Zgradi Oltar + najdi Inลพenirsko uro (Marko) + Medaljon (Elena) โ†’ Duhova Starลกa se pojavita kot Force Ghost, dajata nasvete in permanentne buffe + +**๐ŸงŸ Gronk:** (Demo Locked โ€” izjema: prvih 20 kupcev + Streamerji takoj!) +- Trol z roza dreadlocki, vape v ustih, ima psiฤko Susi (Jazbeฤarko) +- Mentor, govori v 3. osebi: "Gronk... pomaga ลกefu?" + +--- + +### ๐Ÿ›๏ธ FAZA 2 โ€” "Town Restoration & Museum" + +**Cilj:** Raziลกฤi celinski svet, obnovi 27 razruลกenih mest, odpri Muzej + +**Svet:** Celinska Mapa (prej vidna samo kmetija/otok). 27 Razruลกenih Mest razkropljenih po mapi. + +**๐Ÿš๏ธ Town Restoration Loop:** +``` +1. Explore Celino โ†’ Najdi Ruined Town +2. Poberi material (Les, Kamen, ลฝelezo) + ฤas (zombiji pomagajo) +3. Obnovi stavbe: + - Kovaฤnica โ†’ Ivan (Kovaฤ) se vseli + - Pekarna โ†’ Marija (Pekarka) se vseli + - Klinika โ†’ Dr. Chen se vseli + ...in tako naprej +4. Posodi Zombi Delavce NPCjem โ†’ dobijo pomoฤ +5. NPC-ji ti dajejo nagrade + zgodba clues o Ani +``` + +**27 Mest ร— 5-15 hiลก = 300+ stavb za obnovo** +**180 NPC-jev skupaj ฤez vsa mesta** + +**๐Ÿ›๏ธ Muzej:** +- Vodi ga Kustos (NPC) +- Doniraลก redke predmete (Artifakti, Legendarne Ribe, Zlatega Hroลกฤca...) +- Nagrada: Sova prinaลกa darila podnevi, Netopir ponoฤi + +**๐ŸŽฃ Ribnik (domaฤi):** +- Prej samo divje ribarjenje (ocean) +- Zdaj gradiลก ribnik na kmetiji +- Vzreja rib za hrano in prodajo + +**๐Ÿ”ง Napredni Stroji (iz Garaลพe naprej):** +- Avtomatska Zalivalka (Solar powered) +- Bio-Rafinerija (predelava odpadkov v gorivo) +- Konzervarna (hrana za dolgo) + +--- + +### โšก FAZA 3 โ€” "Elektrika & Avtomatizacija" + +**Cilj:** Elektriฤno omreลพje, avtomatska pridelava + +**Vsebina:** +- **Power Grid:** Generator โ†’ Elektriฤni vodi โ†’ Naprave +- **Avtomatizacija:** Kombajn, Robotski kmet, Avtomatski packer +- **Energija:** Solar paneli, Vodni generator (ob reki) +- **Upgrade:** Vse prejลกnje zgradbe dobijo elektriฤno varjanto (2ร— hitrost) + +--- + +### ๐ŸŒ€ FAZA 4 โ€” "Portali & Biomi (Tier 1)" + +**Cilj:** Odpri 9 Normalnih Biome-ov prek portalov + +**9 Normalni Biomi:** +1. ๐ŸŒพ Grassland โ€” Travnik (Spawn area) +2. ๐ŸŒฒ Forest โ€” Gozd (les, volkovi, jeleni) +3. ๐ŸŒฟ Swamp โ€” Moฤvirje (strupeno) +4. ๐Ÿœ๏ธ Desert โ€” Puลกฤava (vroฤina, ลกkorpijoni) +5. ๐Ÿ”๏ธ Mountain โ€” Gora (rudnik, orli) +6. โ„๏ธ Snow โ€” Sneg (mraz, led medvedi) +7. ๐Ÿš๏ธ Wasteland โ€” Puลกฤoba (ruลกevine, mutanti) +8. ๐ŸŒด Tropical โ€” Tropski (plaลพa, kokoลกi) +9. โ˜ข๏ธ Radioactive โ€” Radioaktivno (mutacije, sij) + +**Za vsak biom:** Edinstvene rastline, ลพivali, materiali, Boss, NPCji, Romance opcija + +--- + +### ๐Ÿ‰ FAZA 5 โ€” "Anomalni Biomi (Tier 2)" + +**Cilj:** Odpri 11 Posebnih/Anomalnih Biome-ov + +**11 Anomalni Biomi:** +10. ๐Ÿฆ– Dino Valley โ€” T-Rex, raptorji, dinozavri +11. ๐Ÿ‰ Mythical Highlands โ€” Zmaji, Griffini, Enorogovi +12. ๐ŸŒฒ Endless Forest โ€” Bigfoot, Wendigo, kriptidi +13. ๐Ÿฆ• Loch Ness โ€” Nessie, ล kotska, gradu +14. ๐Ÿ’€ Catacombs โ€” Skeletne armade, duhovi, Nekromancija +15. ๐Ÿบ Egyptian Desert โ€” Piramide, Mumije, Sfinga +16. ๐ŸŒด Amazon Rainforest โ€” Piranhe, Anaconde, Pleme +17. ๐ŸŒŠ Atlantis โ€” Podvodni svet, Morske deklice, Podmornica +18. โ˜ข๏ธ Chernobyl โ€” FINALNA ZONA, Ana je tu! +19. ๐Ÿ‡ฒ๐Ÿ‡ฝ Mexican Cenotes โ€” Aksolotli, Majevske ruลกevine +20. ๐Ÿง™โ€โ™€๏ธ Witch Forest โ€” Baba Jaga, ฤŒarovnice, Magija + +**Za Portal Unlock:** Vsak biom ima edinstven pogoj (npr. Atlantis = popravi potapljaฤko opremo + 7 kristalov) + +--- + +### ๐Ÿ’ FAZA 6 โ€” "Romanca, Otroci & Generacije" + +**Cilj:** Vzpostavi druลพino, generacijsko igranje + +**12 Romance Opcij:** +- Lena (Kmetova hฤerka), Katarina (Trgovka), Sonya (Zdravnikova asistentka)... +- Eksotiฤnih 7: Plemenita Princesa (Amazon), Morska Deklica (Atlantis), Valkira (Mythical), Egipฤanska Sveฤenica, ล kotska Deklica, ฤŒuvajka Dinozavrov, Duh Deklica (Catacombs) + +**Otroci โ€” 5 stopenj rasti:** +1. Dojenฤek (0-1) โ†’ Zibelka, skrb +2. Malฤek (1-3) โ†’ Hodi, igra +3. Otrok (3-10) โ†’ Pomaga na farmi! +4. Najstnik (10-18) โ†’ Polno delo +5. Odrasel (18+) โ†’ **Postane Playable!** + +**Generacije:** Kai โ†’ otroci โ†’ vnuki โ†’ ... 100+ realnih let moลพno + +--- + +### ๐Ÿ—๏ธ FAZA 7 โ€” "Mega Razvoj Mest" + +**Cilj:** Nadgradi vsa 27 mest v polno mesti + +**Town Upgrade System:** +``` +Ruined โ†’ Repaired โ†’ Developed โ†’ Thriving โ†’ City + (Faza 2) (Faza 7) (Endgame) +``` + +**Mega Projekti:** +- Elektrarna za celotno mesto +- ล ola (uฤi otroke, +skills) +- Bolniลกnica (zdravi NPC-je in Kai) +- Trg (teden trลพnica, 50+ prodajalcev) +- Stara Dvorana (NPC odloฤa z Kai-em skupaj) + +--- + +### ๐Ÿ”ฌ FAZA 8 โ€” "Research & Advanced Tech" + +**Cilj:** Napredna tehnologija + Ana's Science System + +**Ana (ko je reลกena v Fazi 8):** +- Odpre Laboratorij na kmetiji +- Research tree: Bio-tech, Radiation tech, Nano-tech +- Crops 2.0 (genetsko modificirane, 2ร— yield) +- Zombie Evolution (zombiji dobijo upgrade) + +**Skrita Klet FULL UPGRADE:** +- Industrijska Gojilnica (200ร— kapaciteta) +- Kemiฤni Laboratorij (kompleksne formule) +- Tveganje: Policija/ลฝupan kaznuje! + +--- + +### โ˜ข๏ธ FAZA 9 โ€” "Chernobyl & The Rescue" + +**Cilj:** Prodri v ฤŒernobil, reลกi Ano + +**Zgodba:** +1. Kai najde vse 9 Key Fragmentov (iz biome bosลกev) +2. Portal do ฤŒernobila se odklene +3. Fight through: Radiation Zones + Zmaj Volk boss (emotionalen) +4. Giant Troll King Phase 1 (HP: 2500) โ€” premagan, beลพi +5. **ANA REล ENA** ๐Ÿ’œ + +**The Reunion:** +``` +Kai enters Ana's cell. +Ana turns around. + +ANA: "...Kai?" +KAI: "Ana... I found you." +[They embrace, crying] +ANA: "You came for me..." +KAI: "Always. Twin Bond, remember?" + +[Twin Bond FULLY AWAKENS โ€” zlatem svit!] +``` + +**Ana postane Playable!** Unlock vseh 6 Twin Bond abilityjev. + +--- + +### ๐ŸŒŸ FAZA 10 โ€” "Final Choice & 4 Endings" (ENDGAME) + +**Cilj:** Premagi finale, izberi konec + +**Dr. Krniฤ‡ Ultimatum:** *"Pridi v Reactor Core. Ali spustim SUPER VIRUS. 7 dni."* + +**Boss Rush:** +1. Dr. Krniฤ‡ (HP: 1500) +2. Giant Troll King Phase 2 (HP: 5000 โ€” Ultimate Form!) + +**CURE MACHINE:** +``` +Dr. Krniฤ‡ (dying): "Edini naฤin zdravila = Alpha Hybrid ลพrtev." +``` + +**4 KONCI โ€” Player Choice:** + +| Konec | Pogoj | Kaj se zgodi | +|-------|-------|--------------| +| ๐Ÿ’” Kai se ลพrtvuje | Pritisni A | Kai umre, Ana vodi novi svet, hฤer imenuje "Kai" | +| ๐Ÿ’” Ana se ลพrtvuje | Pritisni B | Ana umre, Kai vodi novi svet, hฤer imenuje "Ana" | +| ๐Ÿ’€ Dark World | Pritisni C | Oba zavrneta. ลฝivita skupaj veฤno, zombiji ostanejo. | +| ๐ŸŒŸ PERFECT โœจ | Vse 50 Aninih Clues + 9 Key Fragmentov | Ana izraฤuna alternativo โ€” oba preลพivita! | + +**Perfect Ending zadnja scena:** Piknik na kmetiji. Kai, Ana, partnerji, 6 otrok, smeh. +``` +KAI: "We did it." +ANA: "Together. Always." +[Children laughing in background] +``` + +--- + +## ๐Ÿ‘ฅ KOMPLETNI LIKI + +| Lik | Starost | Vloga | Videz | Kdaj | +|-----|---------|-------|-------|------| +| **Kai Markoviฤ‡** | 14 | Protagonist, Alpha Hybrid | Pink/zeleni dreadlocki, rdeฤe oฤi, gaugi, nos ring, hoodie, cunje | Demo+ | +| **Ana Markoviฤ‡** | 14 | Ugrabljena sestra | Kratki temni lasje, bela lab majica, pametna, urejena | Faza 9 (reลกena) | +| **Marko Markoviฤ‡** | ~45 | Mrtev oฤe โ†’ Duh | Sivi lasje, brada, halatna | Faza 1 (Oltar) | +| **Elena Markoviฤ‡** | ~43 | Mrtva mama โ†’ Duh | Dolgi temni lasje, topel nasmeh | Faza 1 (Oltar) | +| **Gronk** | ? | Trol pomoฤnik | Ogromen, roza dreadlocki, vape, Susi psiฤka | Faza 1 | +| **Dr. Krniฤ‡** | ~60 | Zlobneลพ | Mad scientist, halatna, divji pogled | Faza 9-10 | +| **Giant Troll King** | ? | Ugrabil Ano | 3ร— Kai, oklopljen, rdeฤe oฤi | Faza 9-10 | + +--- + +## ๐ŸŽจ VIZUALNI STIL โ€” PRAVILA (NIKOLI KRล I) + +1. **NI PIXEL ART** โ€” High-res smooth ilustracija +2. **Thick black outlines** (~3-5px) na VSEH likih +3. **Gothic Dark Chibi** โ€” veliki glave, majhna telesa, ekspresivne oฤi +4. **Muted gothic palette:** Temne modre, vijoliฤne, rjave + neon accenti (teal, zelena, vijoliฤna) +5. **Transparent ozadje** (Alpha PNG) za VSE like in objekte +6. **Kai DNA โ€” NIKOLI NE MENJAJ:** Pink/zeleni dreadlocki, rdeฤe oฤi, gaugi, nos ring โ€” to so konstante skozi VSO igro in VSE starosti + +--- + +## ๐Ÿ“Š ล TEVILKE + +| | | +|-|-| +| **Faz skupaj** | DEMO + 10 FAZ | +| **Biome-ov** | 20 (9 Normal + 11 Anomalous) | +| **Boss-ov** | 24 (mini-boss/biome + Finalni) | +| **Mest** | 27 (po celinski mapi) | +| **NPC-jev** | 180 (ฤez vsa mesta) | +| **Romance opcij** | 12 | +| **Stopenj rasti otrok** | 5 | +| **Koncev** | 4 | +| **Zombi worker tipov** | 5 | +| **Rastlin** | 80+ | + +--- + +## ๐Ÿ’ฌ KLJUฤŒNI DIALOGJI (Kopiraj direktno) + +**Twin Bond signali (Ana, ลกibki):** +``` +"Kai... ฤutiลก me? Sem ujeta... ampak ลฝIVIM. Ne obupaj!" +"Troll me ฤuva... ampak nisem sama..." +"Kai... najdi me. Twin Bond... deluje..." +``` + +**Gronk:** +``` +"Gronk... pomaga ลกefu?" +"Gronk... dober zombi?" +"Gronk... ลกef... ลพalosten? Gronk pomagal... najti sestro!" +``` + +**Marko (Duh):** +``` +"Kai... Sin... Utri ograjo. Zombiji so ลกibki, ampak horda..." +"Skupaj sva verjela v boljลกi svet. Ne nehaj verjeti." +``` + +**Elena (Duh):** +``` +"Rastline potrebujejo vodo, Kai. Kot ti ljubezen." +"Nisi kriv. Nikoli nisi bil kriv." +``` + +**The Reunion (Faza 9):** +``` +ANA: "...Kai?" +KAI: "Ana... I found you." +ANA: "You came for me..." +KAI: "Always. Twin Bond, remember?" +ANA: "I felt you. Every day. Searching..." +[Twin Bond fully awakens โ€” zlatem svit] +``` + +**Kai dnevnik:** +``` +Dan 1: "Konฤno... Nov dom. Majhna kmetija, ampak dovolj za zaฤetek." +Dan 30: "En mesec. Preลพivim. Ampak Ana... kje si?" +Dan 100: "Sto dni. Pol poti. Drลพim se." +Dan 200: "Dvesto dni. Zdaj se zaฤne pravi boj." +``` + +--- + +*"Twin Bond. Zdaj in vedno."* ๐Ÿ’œ +*ยฉ 2026 HipoDevil666 | Nova Farma / Krvava ลฝetev* +*Posodobljeno: 2026-03-03 ob 14:54* diff --git a/nova farma TRAE/dokumentacija/GEMINI_MASTER_BRIEF_2026_03_03.md b/nova farma TRAE/dokumentacija/GEMINI_MASTER_BRIEF_2026_03_03.md new file mode 100644 index 000000000..74495fe96 --- /dev/null +++ b/nova farma TRAE/dokumentacija/GEMINI_MASTER_BRIEF_2026_03_03.md @@ -0,0 +1,448 @@ +# ๐ŸŽฎ NOVA FARMA (KRVAVA ลฝETEV) โ€” KOMPLETNI MASTER BRIEF +## Za: Gemini / AI asistenta | Datum: 2026-03-03 +## Avtor: David Kotnik (HipoDevil666) + +--- + +## โšก TL;DR (Preberi najprej!) + +**Nova Farma** je dark survival farming RPG z zombie kontrolo, postavljeno v post-apokaliptiฤno Slovenijo leta 2084. Igraลก **Kai Markoviฤ‡a** (14 let), ki je edini preลพiveli alfa hibrid โ€” ugriznjen od zombija, a ne postane eden. Namesto tega dobi moฤ **kontrolirati zombije**. Cilj: preลพivi, zgradi kmetijo, najdi ugrabljeno sestro Ano. + +**Vizualni stil:** 2.5D Dark Chibi Noir โ€” Hand-drawn ilustracija, NI pixel art. Clean lines, thick outlines, muted gothic barve. + +**Platform:** Desktop (Electron + Phaser 3) + +**Inspired by:** Stardew Valley ร— The Last of Us ร— Don't Starve ร— My Time at Portia + +--- + +## ๐ŸŒ SVET + +- **Leto:** 2084 +- **Lokacija:** Slovenija, majhen skriti otok v dolini +- **Svet:** Otok (6400ร—6400px) sredi oceana (10400ร—10400px) +- **Atmosfera:** Post-apokalipsa. Civilizacija je padla. Narava se vraฤa. Zombiji povsod. +- **Vreme:** Deลพ (edini vir ฤiste vode!), sonฤno, nevihta โ€” dinamiฤno + +--- + +## ๐Ÿ‘ฅ LIKI (KOMPLET) + +### ๐Ÿง’ KAI MARKOVIฤ† โ€” Protagonist +- **Starost:** 14 let +- **Spol:** Moลกki +- **Videz:** Pink/zeleni dreadlocki, rdeฤe oฤi (genetska lastnost Markoviฤ‡ linije), gaugi v uลกesih, nos ring, majhni vampirski zobki. Hoodie, raztrgane kavbojke, cunje. Ruksak. Drลพi magiฤno knjigo v eni roki. +- **Osebnost:** Tih, doloฤen, redkobeseden โ€” ko govori, je globoko. Ne joฤe pred drugimi. Skrbi se za druge bolj kot zase. +- **Posebnosti:** + - **Alpha Hybrid:** Ugriznjen a ni postal zombi. Imun. + - **Zombie Control (Alfa Moฤ):** [SPACE] โ†’ kontrolira zombije + - **Twin Bond:** Telepatija z Ano (ฤuti jo, dobiva ลกibke signale) + +### ๐Ÿ‘ง ANA MARKOVIฤ† โ€” Sestra (UGRABLJENA) +- **Starost:** 14 let (Kai-eva dvojฤica) +- **Status:** Ugrabljena od Giant Troll Kinga, zaprta v ฤŒernobilu +- **V igri:** Samo kot glas (Twin Bond signali) v Demo + Faza 1 +- **Karakter:** Genialna, radovedna, optimistiฤna. Nasprotje Kai-a. +- **Posebnost:** Njena genijskost bo reลกila svet (alternativni konec) + +### ๐Ÿ‘จโ€๐Ÿ”ฌ MARKO MARKOVIฤ† โ€” Oฤe (MRTEV, Dan 3) +- **Videz:** Sredoveฤni znanstvenik s sivo-rjavimi lasmi, brada +- **Status:** Umrl od zombie ugrizov Dan 3, brani druลพino +- **V igri:** Duh (Force Ghost) โ€” pojavi se ko Kai zgradi Oltar + najde Inลพenirsko uro +- **Vloga duha:** Inลพenirski nasveti, buff: +15% Craft hitrost + +### ๐Ÿ‘ฉโ€๐Ÿ”ฌ ELENA MARKOVIฤ† โ€” Mama (MRTVA, Dan 3) +- **Videz:** Visoka ลพenska, dolgi temni lasje, topel nasmeh +- **Status:** Umrla isti dan kot Marko +- **V igri:** Duh โ€” pojavi se ko Kai najde Medaljon +- **Vloga duha:** Kmetijski nasveti, buff: +10% Energy regen na kmetiji + +### ๐ŸงŸ GRONK โ€” Prvi zombi pomoฤnik (FAZA 1) +- **Tip:** Trol (udomaฤen velikan, ne navaden zombi) +- **Videz:** Ogromen, roza dreadlocki, vape v ustih (permanent). Vedno chill. +- **Osebnost:** "Chill stric". Govori poฤasi, v 3. osebi. Srฤen pod grdo zunanjostjo. +- **Primer govora:** "Gronk... pomaga ลกefu? Gronk... dober zombi?" +- **Unlock:** Faza 1 (Demo: Locked โ€” razen prvih 20 kupcev + streamerji) +- **Ima psa:** Susi (Jazbeฤarka) โ€” spi pri njegovih nogah + +### ๐Ÿ‘จโ€๐Ÿ”ฌ DR. KRNIฤ† โ€” Glavni zlobneลพ +- **Starost:** ~60 let +- **Videz:** Mad scientist aesthetics โ€” halatna, redki lasje, divji pogled +- **Dejanje:** NAMENOMA sprostil zombie virus iz Black Serpent Laboratory +- **Cilj:** Kontrola nad preostalim svetom +- **V igri:** Faza 2+, finalni boss fight (skupaj z Giant Troll Kingom) + +### ๐Ÿฆ GIANT TROLL KING โ€” Ugrabil Ano +- **Videz:** Ogromen trol, 3ร— veฤji od Kai-a, oklopljen, rdeฤe oฤi +- **Stats:** HP 5000, seizmiฤni napadi, kliฤe zombije, ognjevit dih +- **Lokacija:** ฤŒernobilski Reaktor (Faza 2+) +- **V Demu/Fazi 1:** Samo omenjeni + +--- + +## ๐ŸŽฌ ZGODBA โ€” AKT PO AKT + +--- + +### ๐Ÿ“ผ PROLOG: DAN 0โ€“30 (Pred igro โ€” Intro sekvenca) + +Prikaลพe se samo enkrat, ko prviฤ zagneลก igro: + +**20 Polaroid fotografij z typewriter komentarji:** + +| # | Slika | Komentar | +|---|-------|----------| +| 1 | Sreฤna druลพina pred virusom | "Pred virusom... bili smo sreฤni." | +| 2 | Marko v laboratoriju | "Oฤe je delal na cepilu..." | +| 3 | Red Alert alarm | "Potem je priลกel DAN 0..." | +| 4 | Kaos na ulicah | "Svet se je sesula v 48 urah." | +| 5 | Dom je obkoljen | "DAN 3: Priลกli so k nam..." | +| 6 | Oฤe se bori | "Oฤe: 'Kai... varuj sestro... beลพi...'" | +| 7 | Mama pada | "Mama: 'Ana... Kai... ljubiva vaju...'" | +| 8 | Kai ugriznjen | "Ugriznili so me... ampak nisem postal zombi." | +| 9 | Ana ugrabljena | "Ana: 'KAIII! POMAGAJ!'" | +| 10 | Giant Troll King | "Nekega... POล ASTI jo je odnesel." | +| 11 | Kai sam | "Starลกi mrtvi. Sestra ugrabljena. Sam." | +| 12 | Alfa moฤ se zbudi | "Nekaj se je zbudilo v meni..." | +| 13 | Kai z zombijem | "Zombiji... me posluลกajo?" | +| 14 | Pobeg iz mesta | "Moral sem pobegniti... preลพiveti..." | +| 15 | Potovanje | "7 dni peลกaฤenja skozi peklo..." | +| 16 | Odkrita dolina | "Naลกel sem jo. Mojo dolino." | +| 17 | Zapuลกฤena kmetija | "Majhna kmetija. Nov zaฤetek." | +| 18 | Kai drลพi semena | "ฤŒe bom preลพivel... jo bom naลกel." | +| 19 | Twin Bond signal | "ฤŒutim jo... Ana je ลฝIVA." | +| 20 | Kai zapriseลพe | "Prisegli sem... Najti jo bom. Ne glede na ceno." | + +**Po polaroidih:** Kai se prebudi v Spalni Vreฤi โ†’ blur efekt (amnezija) โ†’ Twin Bond signal โ†’ igra zaฤne. + +--- + +### ๐Ÿ๏ธ AKT 1: DEMO โ€” "PREลฝIVETJE" (Dan 1โ€“200) + +**Cilj:** Preลพivi 200 dni โ†’ Odkleni Fazo 1 + +**Gameplay Loop:** +``` +Jutro โ†’ Zalij rastline โ†’ Zberi pridelek โ†’ Popravi/Zgradi +Podnevi โ†’ Research, Crafting, Explore otok +Zveฤer โ†’ Pospravi, odnesi v skrinje, skozi dialog +Noฤ โ†’ Zpati v ลกotoru (ali Kai omedli = kazen) +``` + +**Zgodba teฤe skozi 3 kanale:** + +1. **KAI DNEVNIK** โ€” ob vsakem Sleep, Kai napiลกe en stavek: + - Dan 1: *"Konฤno... Nov dom. Majhna kmetija, ampak dovolj za zaฤetek."* + - Dan 7: *"En teden sam. Twin Bond signal je bil ลกibak nocoj."* + - Dan 30: *"En mesec. Preลพivim. Kmetija raste. Ampak Ana... kje si?"* + - Dan 100: *"Sto dni. Pol poti. Ana... drลพim se."* + - Dan 200: *"Dvesto dni. Naredil sem, kar sem si obljubil. Zdaj se zaฤne pravi boj."* + +2. **TWIN BOND SIGNALI** โ€” 1ร— na dan, 10% chance, ลกibek Anin glas: + - *"Kai... ฤutiลก me? Sem ujeta... ampak ลฝIVIM."* + - *"Ne obupaj... Twin Bond... deluje..."* + - *"Troll me ฤuva... ampak nisem sama..."* + - Visual: vijoliฤni blisk na zaslonu, Kai zamrzne za sekundo + +3. **FLASHBACK SPOMINI** โ€” Kai stopi 80px blizu posebnih predmetov: + - Star foto โ†’ *"Sreฤni ฤasi"* โ€” Ana z metuljem + - Epruveta โ†’ *"Oฤetov laboratorij"* โ€” "Nikoli ne dotikaj teh vzorฤkov" + - Stara sveฤa โ†’ *"Zadnja veฤerja"* โ€” Anin rojstni dan + +**Omejitve v Demu:** +- Samo ลกotor (ne hiลกa) +- Samo Tier 1 orodja (lesena) +- Max 3 zombiji (ne morejo delati โ€” samo sledijo) +- 2 rastlini: Pลกenica (3 dni) + Korenje (4 dni) +- Voda samo iz deลพja (Rain Catcher) + +--- + +### ๐ŸŒซ๏ธ AKT 2: FAZA 1 โ€” "ODPRTJE" (Dan 200+) + +**Trigger:** 200 dni preลพivetih โ†’ Fog of War se zaฤne dvigovati + +**Nove mehanike:** + +| Mehanika | Opis | +|----------|------| +| **Fog of War** | Svet okrog otoka je bil skrit. Zdaj se odkriva. | +| **Gronk Unlock** | Prvi pravi pomoฤnik (Trol, demo locked) | +| **Scout Zombi** | Edini zombi ki gre v Fog. Vrne se z materiali. | +| **Zombi Workforce** | 5 tipov zombi delavcev z leveli | +| **Ruลกevine** | Garaลพa, Rastlinjak, Hlev โ€” najdeลก in popraviลก | +| **Rudnik** | Majhen rudnik โ€” Baker, Premog, Glina | +| **Crafting napredek** | Motorka, Vodni Filter, Stiskalnica | + +**Zombi Workforce (5 tipov):** + +| Tip | HP | Hitrost | Naloga | Max | Level Up | +|-----|----|---------|--------|-----|----------| +| ๐Ÿ•ต๏ธ Izvidnik (Scout) | 50 | Hiter | Gre v Fog, odkriva mapo, prinaลกa material | 1 | Hitrejลกe iskanje, manj izgub | +| ๐ŸŒพ Kmet (Farmer) | 30 | Poฤasen | Zaliva, ลพanje | 3 | Hitrejลกe, veฤji pridelek | +| ๐Ÿช“ Gozdar (Lumberjack) | 40 | Normalen | Seka drevesa | 2 | Hitrejลกe, veฤ lesa/drevo | +| โ›๏ธ Rudar (Miner) | 60 | Poฤasen | Koplje rudo | 2 | Lv5+ najde ลฝelezo! | +| ๐Ÿ“ฆ Nosaฤ (Porter) | 40 | Normalen | Pobira Drop Boxe โ†’ polaga v skrinje | 1 | Lv7+ nosi 20 predmetov | + +**Drop Box sistem:** +- Zombiji ne nosijo direktno v tvoje skrinje (balance) +- Delo โ†’ Drop Box โ†’ Ti pobereลก โ†’ Tvoj inventar +- 3 Drop Boxy: ob Workshopu, Rudniku, Farmi + +**Parental Ghost Quest:** +``` +Kai zgradi Oltar (20 Kamen + 10 Les + 2 Sveฤi) ++ Najde Inลพenirsko uro (Markov predmet) โ†’ Marko se pojavi ++ Najde Medaljon (Elenin predmet) โ†’ Elena se pojavi + +Marko: "Kai... Sin... Utri ograjo." +Elena: "Rastline potrebujejo vodo, Kai. Kot ti ljubezen." +Buffi so stalni ko si na kmetiji. +``` + +--- + +### ๐Ÿ”ต AKT 3: FAZA 2+ โ€” "ISKANJE ANG" (Prihodnost) + +**Unlock:** Faza 2 = Nova obmoฤja, portali do biome-ov + +**20 Biome-ov:** + +**Tier 1 โ€” Normalni:** +Grassland, Forest, Swamp, Desert, Mountain, Snow, Wasteland, Tropical, Radioactive + +**Tier 2 โ€” Anomalni (Super rare portali):** +Dino Valley ๐Ÿฆ–, Mythical Highlands ๐Ÿ‰, Endless Forest ๐ŸŒฒ, Loch Ness ๐Ÿฆ•, Catacombs ๐Ÿ’€, Egyptian Desert ๐Ÿบ, Amazon ๐ŸŒด, Atlantis ๐ŸŒŠ, Chernobyl โ˜ข๏ธ, Mexican Cenotes ๐Ÿ‡ฒ๐Ÿ‡ฝ, Witch Forest ๐Ÿง™โ€โ™€๏ธ + +**Chernobyl = FINAL ZONE**: +- Kai najde Ano v Reactor Core +- Boss Fight: Dr. Krniฤ‡ + Giant Troll King (Phase 1) +- **ANA REล ENA** โ€” postane playable character + +--- + +### ๐Ÿ’œ AKT 4: REUNIJA & KONEC (Faza 3) + +**THE REUNION:** +``` +Kai enters Ana's cell. +Ana turns around. + +ANA: "...Kai?" +KAI: "Ana... I found you." +[They embrace] +ANA: "You came for me..." +KAI: "Always. Twin Bond, remember?" + +[Twin Bond FULLY AWAKENS โ€” zlatem svit] +``` + +**Ana se pridruลพi** โ€” dobita Twin Bond sposobnosti: +- Twin Telepathy (komunicirata brez razdalje) +- Twin Strike (skupni napad, 2ร— poลกkodbe) +- Twin Shield (preneseta damage drug na drugega) +- Twin Sense (vidita skozi zidove) +- Resurrection Ultimate (vsak reลกi drugega 1ร—/dan) + +**4 konci (player choice):** +``` +Dr. Krniฤ‡ (dying): "Edini naฤin zdravila zahteva Alpha Hybrid ลพrtev." + +[A] Kai se ลพrtvuje โ†’ Ana vodi novi svet, ima hฤer "Kai" +[B] Ana se ลพrtvuje โ†’ Kai vodi novi svet, ima hฤer "Ana" +[C] Oba zavrneta โ†’ ลฝivita skupaj na kmetiji veฤno, zombiji ostanejo +[D] Alternativa* โ†’ Oba preลพivita! Svet ozdravljan. PERFECT ENDING โœจ + *Samo z vsemi 50 Aninimi clues + 9 Key Fragmenti +``` + +--- + +## โš™๏ธ GAME SISTEMI (Komplet) + +### ๐ŸŒฑ Farming +- **Saditev:** Kai ore tla (E tipka) โ†’ posadi semena โ†’ zaliva vsak dan โ†’ ลพanje +- **Rastline umrejo** brez vode po 1 dnevu +- **Demo:** Pลกenica (3 dni), Korenje (4 dni) +- **Faza 1+:** Konoplja (7 dni, MAIN ECONOMY), Sonฤnice, 80+ other crops + +### ๐Ÿ’ง Voda +- **VSA VODA JE STRUPENA** (ocean, reke, luลพe) +- **EDINI VIR:** Deลพevnica (Rain Catcher) +- Brez deลพja = kriza โ†’ stockpile! +- **Faza 1:** Vodni Filter (Oglje+Pesek+Plastika) โ†’ mora voda postane pitna + +### ๐Ÿ  Housing +| Tier | Struktura | Energy restore | +|------|-----------|---------------| +| 0 | Spalna Vreฤa | 40% | +| 1 | ล otor โ›บ | 70% (Demo MAX) | +| 2 | Lesena Koฤa ๐Ÿš๏ธ | 85% (Faza 1 MAX) | +| 3 | Kamnita Hiลกa ๐Ÿ  | 95% | +| 4 | Moderna Hiลกa ๐Ÿก | 100% | + +### ๐Ÿ•ฐ๏ธ Dan/Noฤ +- 1 in-game dan = 20 realnih minut +- **Noฤ:** Zombiji 2ร— moฤnejลกi, veฤ spawn-ov +- **02:00:** Kai mora spati (ali omedli = energy loss + money loss) + +### ๐ŸงŸ Zombie Taming +- [SPACE] blizu zombija โ†’ Alfa Moฤ โ†’ Screen flash โ†’ Zombij podrejen +- Demo: Max 3 zombiji +- Faza 1: Max 8 zombijev +- Assign Task: Interact z zombijem โ†’ izberi nalogo + +### ๐Ÿฉธ Survival Stats +- **HP:** 100 max, regeneracija s hrano +- **Energy:** 100%, dreni z delom, regenerira s spanjem/hrano +-ไฝŽ Energy (< 20%): Kai se upoฤasni + +### ๐Ÿ”ง Crafting +- **Campfire:** Osnovno kuhanje +- **Workbench (Garaลพa Faza 1):** Motorka, Laksarca, Filter +- **Furnace:** Sand + Coal = Glass, Iron Ore = Iron Bar + +--- + +## ๐ŸŽจ VIZUALNI STIL โ€” PRAVILA ZA GENERIRANJE ASSETOV + +### ABSOLUTNA PRAVILA (nikoli krลกi): +1. **NI PIXEL ART** โ€” High-res ilustracija, smooth lines +2. **Thick black outlines** na vseh likih (~3-5px) +3. **Gothic Dark Chibi** stil โ€” veliki glave, majhna telesa, ekspresivne oฤi +4. **Muted gothic palette:** Temne modre, vijoliฤne, rjave, sive + neon accenti (teal, zelena) +5. **Transparentno ozadje** (Alpha 24 PNG) za vse like in predmete +6. **Kai DNA:** Vedno pink/zeleni dreadlocki, rdeฤe oฤi, gaugi, nos ring โ€” nikoli drugaฤen! + +### Skale (relativno na Kai = 64px visok): +- Gronk: 2.5ร— Kai = 160px +- ล otor: 2.5ร— Kai = 160px +- Hiลกa: 3ร— Kai = 192px +- Garaลพa/Hlev: 4ร— Kai = 256px +- Piลกฤanec: 0.2ร— Kai = 13px +- Kura: 0.5ร— Kai = 32px +- Krava: 2ร— Kai = 128px + +### Y-Sorting (globina): +- Vse se sortira po Y koordinati nog +- Kai hodi ZA drevesi ko je viลกje na ekranu +- Kai hodi PRED drevesi ko je niลพje na ekranu + +--- + +## ๐Ÿ“‹ FAZE โ€” KDAJ SE KAJ ZGODI + +``` +โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” +โ”‚ DEMO โ”‚ Dan 1โ€“200 โ”‚ Preลพivi, kmetuj, spoznaj โ”‚ +โ”‚ โ”‚ โ”‚ otok, Twin Bond signali โ”‚ +โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค +โ”‚ FAZA 1 โ”‚ Dan 200+ โ”‚ Fog se dvigne, Gronk, โ”‚ +โ”‚ (Purchase) โ”‚ โ”‚ Zombi delavci, Ruลกevine, โ”‚ +โ”‚ โ”‚ โ”‚ Rudnik, Scout โ”‚ +โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค +โ”‚ FAZA 2 โ”‚ After Faza 1 โ”‚ Portali, 20 Biome-ov, โ”‚ +โ”‚ โ”‚ โ”‚ Ana lokacija razjasnjena โ”‚ +โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค +โ”‚ FAZA 3 โ”‚ End Game โ”‚ ฤŒernobil, Ana reลกena, โ”‚ +โ”‚ โ”‚ โ”‚ Twin Bond full power, โ”‚ +โ”‚ โ”‚ โ”‚ Dr. Krniฤ‡ boss โ”‚ +โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค +โ”‚ KONEC โ”‚ Player Choice โ”‚ 4 razliฤni konci โ”‚ +โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ +``` + +--- + +## ๐Ÿ› ๏ธ TEHNIฤŒNI STACK + +- **Engine:** Phaser 3 (JavaScript) +- **Wrapper:** Electron (Desktop app, macOS/Windows/Linux) +- **Resolucija:** 1920ร—1080 +- **Svet:** 10400ร—10400px (otok 6400ร—6400px center) +- **Fizika:** Phaser Arcade Physics (brez gravitacije) +- **Audio:** Web Audio API (proceduralni) + .ogg za glasbo +- **Save:** localStorage (za demo), JSON save file (Faza 1+) + +### Scene struktura: +``` +MenuScene โ†’ (IGRAJ gumb) โ†’ GrassSceneClean + UIScene (overlay) + โ†’ (prihodnost) โ†’ IntroScene โ†’ GrassSceneClean +``` + +--- + +## ๐ŸŽฏ KAR JE ZDAJ V IGRI (2026-03-03) + +| Sistem | Status | +|--------|--------| +| Phaser 3 + Electron wrapper | โœ… | +| Otok (50ร—50 tilov, jagged obod) | โœ… | +| Ocean (animiran, pena) | โœ… | +| Kai โ€” hoja vse 4 smeri | โœ… | +| Kai โ€” Y-sorting | โœ… | +| Drevesa (3 stopnje rasti) | โœ… | +| Proceduralni deลพ (160 ฤrtiฤnih kapljic) | โœ… | +| **Glavni meni (MenuScene)** | โœ… | +| **Building System [B]** | โœ… ghost preview + Y-sort + collider | +| **UIScene** (HP, energy, hotbar, minimap, inventory) | โœ… | +| Farming sistem | โŒ | +| Zombi AI | โŒ | +| Dan/Noฤ cikel | โŒ | +| Inventar logika | โŒ | +| Intro sekvenca (20 polaroidov) | โŒ | +| Twin Bond signali | โŒ | +| Flashback spomini | โŒ | + +--- + +## ๐Ÿ’ฌ KLJUฤŒNI DIALOGI (kopiraj direktno) + +### Twin Bond signali (Ana): +``` +"Kai... ฤutiลก me? Sem ujeta... ampak ลฝIVIM. Ne obupaj!" +"Ne obupaj... Twin Bond... deluje..." +"Troll me ฤuva... ampak nisem sama..." +"Kai... najdi me..." +``` + +### Gronk: +``` +"Gronk... pomaga ลกefu?" +"Gronk... dober zombi?" +"Gronk... sadi... rastline..." +"Gronk... ลกef... ลพalosten? Gronk... pomagal... najti sestro!" +"Gronk... rad... ogenj. Toplo." +``` + +### Marko (duh): +``` +"Kai... Sin... Utri ograjo." +"Skupaj sva verjela v boljลกi svet. Ne nehaj verjeti." +``` + +### Elena (duh): +``` +"Rastline potrebujejo vodo, Kai. Kot ti ljubezen." +"Nisi kriv. Nikoli nisi bil kriv." +``` + +### Kai (dnevnik): +``` +Dan 1: "Konฤno... Nov dom. Majhna kmetija, ampak dovolj za zaฤetek." +Dan 30: "En mesec. Preลพivim. Ampak Ana... kje si?" +Dan 200: "Dvesto dni. Zdaj se zaฤne pravi boj." +``` + +### THE REUNION: +``` +Kai: "Ana... I found you." +Ana: "...Kai? You came for me..." +Kai: "Always. Twin Bond, remember?" +Ana: "I felt you. Every day. Searching..." +[Twin Bond fully awakens] +``` + +--- + +*"Twin Bond. Zdaj in vedno."* ๐Ÿ’œ +*ยฉ 2026 HipoDevil666 | Nova Farma / Krvava ลฝetev* diff --git a/nova farma TRAE/dokumentacija/STATUS_DEMO_FAZA1_2026_03_03.md b/nova farma TRAE/dokumentacija/STATUS_DEMO_FAZA1_2026_03_03.md new file mode 100644 index 000000000..526313495 --- /dev/null +++ b/nova farma TRAE/dokumentacija/STATUS_DEMO_FAZA1_2026_03_03.md @@ -0,0 +1,300 @@ +# ๐ŸŽฎ NOVA FARMA โ€” KOMPLET STATUS PLAN +## Demo + Faza 1 | Posodobljeno: 2026-03-03 + +--- + +> [!IMPORTANT] +> Ta dokument je **ลพivi status** โ€” tukaj je vse kar je narejeno, kaj manjka, in v kakลกnem vrstnem redu se dela. + +--- + +## ๐Ÿ—บ๏ธ BIG PICTURE โ€” Kaj je igra? + +**Nova Farma (Mrtva Dolina)** je survival farming RPG v Phaser 3 / Electron. + +``` +DEMO โ†’ ฤez 200 dni se odklene โ†’ FAZA 1 โ†’ Faza 2+ (prihodnost) +``` + +- **Platforma:** Desktop (Electron + Phaser 3) +- **Svet:** Otok 50ร—50 tilov (6400px) v oceanu (10400px) +- **Protagonista:** Kai Markoviฤ‡ (14 let) | sestra Ana ugrabljena +- **Vizualni stil:** 2.5D Hand-Drawn / Dark Chibi Noir (NI pixel art!) + +--- + +## โœ… KAJ JE ลฝE NAREJENEGA (Engine & World) + +### ๐ŸŸข DELUJE ZDAJ (2026-03-03) + +| Sistem | Status | Opomba | +|--------|--------|--------| +| Electron + Phaser 3 wrapper | โœ… Dela | Stabilno | +| Otok (50ร—50 grid, jagged obod) | โœ… Dela | 6400ร—6400px | +| Ocean (deep blue ozadje) | โœ… Dela | Klif efekt | +| Pena ob otoku (foam animation) | โœ… Dela | Animirana | +| Kai โ€” hoja vse 4 smeri | โœ… Dela | Walk sheet 16 frames | +| Kai โ€” Y-sorting (globina) | โœ… Dela | `setDepth(this.y)` | +| Kai โ€” trki z robom otoka | โœ… Dela | Physics bounds | +| Zoom (mouse wheel 0.2โ€“5ร—) | โœ… Dela | | +| Tla (grass tile grid) | โœ… Dela | Island mask | +| Drevesa (sapling โ†’ adult) | โœ… Dela | 3 stopnje rasti | +| Proceduralni deลพ | โœ… Dela | 160 ฤrtiฤnih kapljic, naraven | +| ๐Ÿ†• Building System [B] | โœ… Dela | Ghost + Y-sort + collider | +| Auto-loader vegetacije | โœ… Dela | 28px/160px pixel-perfect | + +### ๐ŸŸก DELNO / OKVIRNO + +| Sistem | Status | Kaj manjka | +|--------|--------|-----------| +| GrassScene โ€” rast trave | ๐ŸŸก Partial | Deluje, ampak brez vizualnega feedback-a | +| Gronk sprite | ๐ŸŸก Asset je | Koda odstranjena (clean start) | +| Rain Catcher asset | ๐ŸŸก Asset je | Ni integriran v gameplay | + +--- + +## โŒ DEMO โ€” KAJ MANJKA (Prioritetni seznam) + +Demo = **prvo kar bo videl igralec**. Mora vsebovati: + +### ๐Ÿ”ด KRITIฤŒNO โ€” Brez tega Demo ne more iti ven + +#### 1. ๐ŸŽฌ INTRO SEKVENCA (20 Polaroid slik) +- [ ] `polaroid_01_druzina.png` โ€” Druลพina pred virusom +- [ ] `polaroid_02_lab.png` โ€” Oฤe v laboratoriju +- [ ] `polaroid_03_izbruh.png` โ€” Red Alert +- [ ] `polaroid_04_kaos.png` โ€” Kaos na ulicah +- [ ] `polaroid_05_dom.png` โ€” Dom obkoljen +- [ ] `polaroid_06_oce.png` โ€” Oฤe se bori +- [ ] `polaroid_07_mama.png` โ€” Mama pada +- [ ] `polaroid_08_kai_ugriznjen.png` โ€” Kai ugriznjen +- [ ] `polaroid_09_ana.png` โ€” Ana ugrabljena +- [ ] `polaroid_10_trol.png` โ€” Veliki Trol Kralj +- [ ] `polaroid_11_sam.png` โ€” Sam +- [ ] `polaroid_12_alfa.png` โ€” Alfa Moฤ +- [ ] `polaroid_13_zombi.png` โ€” Prvi kontrolirani zombi +- [ ] `polaroid_14_pobeg.png` โ€” Pobeg +- [ ] `polaroid_15_potovanje.png` โ€” Potovanje +- [ ] `polaroid_16_dolina.png` โ€” Najdena dolina +- [ ] `polaroid_17_kmetija.png` โ€” Opuลกฤena kmetija +- [ ] `polaroid_18_seme.png` โ€” Prvi semenski +- [ ] `polaroid_19_bond.png` โ€” Twin Bond +- [ ] `polaroid_20_obljuba.png` โ€” Obljuba +- [ ] **Koda:** IntroScene.js โ€” prikaz polaroidov s tipografiko + +#### 2. ๐ŸŒฑ FARMING SISTEM +- [ ] Kai puede s Hoem (E tipka na tleh) +- [ ] `crop_wheat_stage0-3.png` โ€” Pลกenica (4 stopnje rasti) +- [ ] `crop_carrot_stage0-4.png` โ€” Korenje (5 stopenj rasti) +- [ ] Zalivanje (Zalivalka iz inventarja) +- [ ] Harvest (klik na zrelo rastlino) +- [ ] Rastlina umre brez vode po 1 dnevu + +#### 3. ๐ŸงŸ ZOMBI SISTEM (Basic) +- [ ] `shambler_walk_sheet.png` โ€” Osnova zombi (4 smeri ร— 4 frame) +- [ ] Spawn zombijev (max 3 na otoku) +- [ ] AI: Shambler โ€” poฤasi sledi Kai-u +- [ ] SPACE tipka โ†’ Alfa Moฤ โ†’ zombi udomaฤen +- [ ] Udomaฤen zombi "sledi" Kai-u + +#### 4. ๐Ÿ•ฐ๏ธ DAN/NOฤŒ CIKEL +- [ ] 20-minutni cikel (1 in-game dan) +- [ ] Vizualna sprememba svetlobe (ambient tint) +- [ ] Noฤ: Kai mora v ลกotor (ali omedli) +- [ ] ล otor: Sleep โ†’ preskoฤi na jutro + +#### 5. โค๏ธ UI / HUD +- [ ] Zdravje (5 src, HP bar) +- [ ] Energija (Energy bar) +- [ ] Dan + Ura (top right) +- [ ] Trial Version banner (top left) +- [ ] Inventar ozadje (hotbar bottom) +- [ ] Inventar popup (I tipka) + +#### 6. ๐Ÿ“ฆ START INVENTAR +- [ ] Kai se pojavi z: Kruhร—1, Jabolkoร—1, Baklaร—1, Semena pลกenicaร—10, korenjeร—5 +- [ ] Zaฤetna skrinja na kmetiji (chest object + open menu) + +#### 7. ๐Ÿง  AMNEZIJA SISTEM +- [ ] Blur efekt ob startu +- [ ] 3 Memory Fragments (blizu predmetov) +- [ ] Screen glitch flash โ†’ Polaroid spomin + +--- + +### ๐ŸŸ  VISOKA PRIORITETA โ€” Demo je bolj popoln z njimi + +#### 8. ๐ŸŒง๏ธ RAIN CATCHER (Lovilec deลพevnice) +- [ ] Integracija rain_catcher.png v gameplay +- [ ] Ko deลพuje โ†’ zbira vodo +- [ ] Voda potrebna za zalivanje in pitje + +#### 9. ๐Ÿ  ล OTOR (Tent) โ€” SLEEP SISTEM +- [ ] Klik na ลกotor โ†’ "Spati?" dialog +- [ ] Potrditev โ†’ Fade to black โ†’ jutro (preskoฤi noฤ) +- [ ] Energy restore: 70% + +#### 10. ๐Ÿ’ฌ DIALOG + TYPEWRITER +- [ ] Typewriter efekt za tekst +- [ ] Prvnรญ dialog: Gronk (tutorial NPC) +- [ ] Twin Bond sporoฤila od Ane (random 1/dan) + +#### 11. ๐ŸŽต ZVOK +- [ ] Proceduralni zvok deลพja (Web Audio ลกum) +- [ ] Koraฤaji (`footstep.ogg` ali proceduralni) +- [ ] Sekanje dreves +- [ ] Ambientna glasba (ลพuborenje oceana) + +--- + +## ๐Ÿ”ต FAZA 1 โ€” Odkleni se po 200 dneh + +> Faza 1 se zaฤne na **istem save filu**, samo se odklene vsebina. + +### FAZA 1 CHECKLIST + +#### Svet & Mapa +- [ ] **Fog of War** โ€” skrita mapa okrog otoka +- [ ] **Fog odpiranje** โ€” Zombi Izvidnik razkrije obmoฤja +- [ ] **3 Ruลกevine** v fogu: Garaลพa, Rastlinjak, Hlev +- [ ] **Rudnik** (5ร—5 area, vhod zruลกen) + +#### Zombi Workforce (5 tipov) +- [ ] ๐Ÿ•ต๏ธ **Izvidnik (Scout)** โ€” Gre v Fog, prinese material, odpira meglo (Max 1) +- [ ] ๐ŸŒพ **Kmet (Farmer)** โ€” Zaliva, ลพanje (Max 3) +- [ ] ๐Ÿช“ **Gozdar (Lumberjack)** โ€” Seka drevesa (Max 2) +- [ ] โ›๏ธ **Rudar (Miner)** โ€” Koplje rudo, Lv5+ najde ลพelezo (Max 2) +- [ ] ๐Ÿ“ฆ **Nosaฤ (Porter)** โ€” Pobira Drop Boxe โ†’ polaga v skrinje (Max 1) + +#### Drop Box Sistem +- [ ] Drop Box objekt (Zombiji odlagajo sem) +- [ ] 3 lokacije: ob workshopu, rudniku, farmi +- [ ] Kai pobere roฤno iz Drop Boxa + +#### Crafting (v Garaลพi) +- [ ] ๐Ÿชš **Motorka/Laksarca** (Motor + Veriga + Glava + Bencin) +- [ ] ๐ŸŒฟ **Trav-rezalnik** (Motor + Rotating Head + Palica) +- [ ] ๐Ÿ’ง **Vodni Filter** (Oglje + Pesek + Plastika) โ€” Ocean water โ†’ Clean water + Sol +- [ ] ๐Ÿ›ข๏ธ **Stiskalnica** (Ironร—5 + Woodร—20 + Stoneร—10) โ€” Sonฤnice โ†’ Bio-olje + +#### Rudarjenje +- [ ] Kop z Motiko (E blizu kamnine โ†’ zbira material) +- [ ] Baker (Copper) โ€” common +- [ ] Premog (Coal) โ€” common +- [ ] Glina (Clay) โ€” blizu vode +- [ ] ลฝelezo (Iron) โ€” samo Rudar Lv5+! + +#### ลฝivali (Nakup) +- [ ] Dve ลพivali dostopni v Fazi 1: Kura + Krava +- [ ] Delivery sistem (ลพivali pridejo v ลกkatli) +- [ ] Kokoลกnjak (Coop) za Kure +- [ ] Hlev (Barn) za Krave + Gronka + Susi + +#### Nove Crops (Faza 1 odkleni) +- [ ] Konoplja (Cannabis) โ€” 7 dni, Main Economy +- [ ] Sonฤnice (Sunflowers) โ†’ Bio-olje +- [ ] Krompir, Paradiลพnik, Buฤke + 80+ more (postopno) + +#### Gronk & Duhovi +- [ ] Gronk odkleni v Fazi 1 (Demo: Locked) + - Izjema: Prvih 20 kupcev + Streamerji โ†’ takoj! +- [ ] Susi Jazbeฤarka (Gronkova psiฤka) +- [ ] Oltar โ†’ Duh Oฤeta in Matere + +--- + +## ๐Ÿ“Š REALNI NAPREDEK (2026-03-03) + +``` +DEMO NAPREDEK: +โ–ˆโ–ˆโ–ˆโ–ˆโ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘ ~20% + +Kaj je: Svet, Kai, Drevesa, Building sistem, Deลพ +Kaj ni: Farming, Zombi AI, UI, Intro, Dan/Noฤ, Inventar + +FAZA 1 NAPREDEK: +โ–ˆโ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘ ~5% + +Kaj je: Building mood (temelj za zgradbe) +Kaj ni: Vse ostalo +``` + +--- + +## ๐ŸŽฏ PRIPOROฤŒENI VRSTNI RED DELA + +### ๐Ÿ“… SPRINT 1 โ€” "Osnove Preลพivetja" (~2โ€“3 seje) +1. **HUD** โ€” hearts, energy bar, dan/ura text (brez slike, samo tekst ok) +2. **Inventar** โ€” Kai dobi start items ob spawnu +3. **ล otor** โ€” Sleep interakcija (fade โ†’ jutro) +4. **Dan/Noฤ** โ€” ambient tint + ura teฤe + +### ๐Ÿ“… SPRINT 2 โ€” "Kmetovanje" (~3โ€“4 seje) +5. **Tla Farming** โ€” Kai hora s hoem (E tipka) +6. **Crop assets** โ€” Wheat (4 stopnje) + Carrot (5 stopenj) generate +7. **Planting + Watering** โ€” logika rasti +8. **Harvest** โ€” klik na zrelo โ†’ inventar + +### ๐Ÿ“… SPRINT 3 โ€” "Zombi" (~2โ€“3 seje) +9. **Shambler sprite** โ€” generirati walk sheet +10. **Zombi AI** โ€” follow Kai, basic pathfinding +11. **Alfa Moฤ** (SPACE) โ€” udomaฤitev + +### ๐Ÿ“… SPRINT 4 โ€” "Zgodba" (~2 seje + asset generiranje) +12. **20 Polaroid slik** โ€” generirati z AI +13. **IntroScene.js** โ€” prikaz polaroidov +14. **Amnezija sistem** โ€” blur + 3 spomini + +### ๐Ÿ“… SPRINT 5 โ€” "Demo Polish" (~1โ€“2 seje) +15. **Rain Catcher** โ€” gameplay integracija +16. **Zvoki** (proceduralni ali AI generated) +17. **Trial Version UI** banner + +> ล ele po tem โ†’ Faza 1 razvoj! + +--- + +## ๐Ÿ“ ASSET STATUS + +### โœ… Imamo +- `kai_walk_sheet.png` โ€” 16 frame walk (4 smeri ร— 4) +- `gronk_walk_sheet.png` โ€” asset je, koda disabled +- `sotor.png`, `campfire` (taborni_ogenj), `rain_catcher`, `foundation_concrete` +- `tree_adult_0-5.png`, `drevo_faza_1/2/veliko.png` +- `grass_ref_1`, `visoka_trava_v2`, `grass_cluster_*` +- `dead_nature_0-8.png`, `fence_sign_0-2.png` +- `rain_drops.png` +- UI asseti: hotbar, inventory_panel, health_bar, dialog_panel, itd. + +### โŒ Manjka (DEMO KRITIฤŒNO) +- `shambler_walk_sheet.png` (zombi, 4 smeri ร— 4 frame) +- `crop_wheat_stage0-3.png` (4 stopnje rasti) +- `crop_carrot_stage0-4.png` (5 stopenj rasti) +- `polaroid_01-20.png` (20 polaroidnih slik โ€” biggest work) +- `heart_full.png` / `heart_empty.png` (za UI) +- `sleeping_bag_interact.png` (animirana verzija) + +### โŒ Manjka (FAZA 1) +- `shambler_idle.png`, `shambler_tamed.png` +- `zombie_scout_*.png`, `zombie_farmer_*.png`, itd. +- `garage_ruin.png`, `greenhouse_ruin.png`, `barn_ruin.png` +- `drop_box.png` +- `konoplja_stage0-7.png` +- Kura, Krave spritei + +--- + +## ๐Ÿ’ก SKUPNI POVZETEK + +| Faza | Napredek | Ostaja | +|------|----------|--------| +| **Svet/Engine** | ๐ŸŸข 85% | Samo polish | +| **Demo โ€” Gameplay** | ๐Ÿ”ด 15% | Farming, AI, Inventar | +| **Demo โ€” Vsebina** | ๐Ÿ”ด 5% | Intro, dialogi, UI | +| **Demo โ€” Assets** | ๐ŸŸก 30% | Manjka farming + zombi sprites | +| **Faza 1** | ๐Ÿ”ด 5% | Praktiฤno vse | + +--- + +*Posodobljeno: 2026-03-03 ob 13:04* +*Za detajle: `DEMO_FAZA1_COMPLETE.md` | `GAME_BIBLE_FINAL_2026.md`* diff --git a/nova farma TRAE/dokumentacija/ZGODBA_IMPLEMENTACIJA_2026_03_03.md b/nova farma TRAE/dokumentacija/ZGODBA_IMPLEMENTACIJA_2026_03_03.md new file mode 100644 index 000000000..2a0f9c7d3 --- /dev/null +++ b/nova farma TRAE/dokumentacija/ZGODBA_IMPLEMENTACIJA_2026_03_03.md @@ -0,0 +1,394 @@ +# ๐Ÿ“– NOVA FARMA โ€” KOMPLETNA ZGODBA & IMPLEMENTACIJSKI PLAN +## Krvava ลฝetev | Verzija: 2026-03-03 + +> Tukaj je VSE: zgodba, kako se prikaลพe v igri, v kakลกnem vrstnem redu jo gradiva, in kateri assets & koda so potrebni za vsak del. + +--- + +## ๐ŸŒ SVET & KONTEKST + +| | | +|-|-| +| **Leto** | 2084 | +| **Lokacija** | Slovenija โ†’ Skriti otok v Dolini | +| **ลฝanr** | Survival Farming RPG + Zombie Control | +| **Core Theme** | Preลพivetje, Iskanje sestre, Obnova civilizacije | +| **Visual Style** | 2.5D Dark Chibi Noir | + +--- + +## ๐Ÿ‘ฅ LIKI + +| Lik | Starost | Vloga | Status v Demu | +|-----|---------|-------|--------------| +| **Kai Markoviฤ‡** | 14 | Protagonist, Alpha Hybrid | โœ… Player character | +| **Ana Markoviฤ‡** | 14 | Dvojฤica, UGRABLJENA | ๐Ÿ’œ Samo glas (Twin Bond) | +| **Marko Markoviฤ‡** | ~45 | Oฤe, znanstvenik | ๐Ÿ‘ป Duh (po oltar questu) | +| **Elena Markoviฤ‡** | ~43 | Mama, znanstvenica | ๐Ÿ‘ป Duh (po oltar questu) | +| **Gronk** | ? | Zombi, prvi pomoฤnik | ๐ŸŸก Faza 1 unlock | +| **Dr. Krniฤ‡** | ~60 | Glavni zlobnik | ๐Ÿ”’ Faza 2+ | +| **Giant Troll King** | ? | Ugrabil Ano | ๐Ÿ”’ Faza 2+ | + +### Kai โ€” DNA & Osebnost +- **Lasje:** Pink/Zeleni dreadlocki +- **Oฤi:** Rdeฤe (genetska lastnost Markoviฤ‡ linije) +- **Outfit:** Raztrgane cunje (start), mogoฤe upgrade +- **Karakter:** Tih, doloฤen, redko govori โ€” ampak ko govori, je globoko +- **Posebnost:** Alfa Hybrid โ€” ugriznjen, a ni postal zombi โ†’ dobi moฤ *kontrolirati* zombije + +### Ana โ€” Zakaj je vse drugaฤe brez nje +- Kai jo ฤuti skozi **Twin Bond** (pasivna telepatija) +- Random sporoฤila enkrat na dan (redka, ลกibka) +- Ves Kai-ev gameplay je motiviran z "najti Ano" + +--- + +## ๐ŸŽฌ ZGODBA โ€” AKT PO AKT + +--- + +### ๐Ÿ“ผ PROLOG (Pred igro โ€” Intro Sekvenca) + +**Prikaลพe se samo enkrat ob prvem zagonu** โ€” 20 Polaroid slik s typewriter komentarji. + +``` +[Slika 1] Druลพina pred virusom โ†’ "Pred virusom... bili smo sreฤni." +[Slika 2] Oฤe v laboratoriju โ†’ "Oฤe je delal na cepilu..." +[Slika 3] Red Alert โ†’ "Potem je priลกel DAN 0..." +[Slika 4] Kaos na ulicah โ†’ "Svet se je sesula v 48 urah." +[Slika 5] Dom obkoljen โ†’ "DAN 3: Priลกli so k nam..." +[Slika 6] Oฤe se bori โ†’ "Oฤe: 'Kai... varuj sestro... beลพi...'" +[Slika 7] Mama pada โ†’ "Mama: 'Ana... Kai... ljubiva vaju...'" +[Slika 8] Kai ugriznjen โ†’ "Ugriznili so me... ampak nisem postal zombi." +[Slika 9] Ana ugrabljena โ†’ "Ana: 'KAIII! POMAGAJ!'" +[Slika 10] Troll King โ†’ "Nekega... POล ASTI jo je odnesel." +[Slika 11] Sam โ†’ "Starลกi mrtvi. Sestra ugrabljena. Sam." +[Slika 12] Alfa Moฤ โ†’ "Nekaj se je zbudilo v meni..." +[Slika 13] Prvi zombi โ†’ "Zombiji... me posluลกajo?" +[Slika 14] Pobeg โ†’ "Moral sem pobegniti... preลพiveti..." +[Slika 15] Potovanje โ†’ "7 dni peลกaฤenja skozi peklo..." +[Slika 16] Dolina โ†’ "Naลกel sem jo. Mojo dolino." +[Slika 17] Kmetija โ†’ "Majhna kmetija. Nov zaฤetek." +[Slika 18] Semena โ†’ "ฤŒe bom preลพivel... jo bom naลกel." +[Slika 19] Twin Bond โ†’ "ฤŒutim jo... Ana je ลฝIVA." +[Slika 20] Obljuba โ†’ "Prisegli sem... Najti jo bom. Ne glede na ceno." +``` + +**Po polaroidih:** +- Fade to black +- Kai se prebudi v **Spalni vreฤi** โ† prvi frame igre +- Vid zamegljen (amnezija blur efekt) +- Zveni ลกibak signal: *"Kai... sem tu..."* (Ana) +- Blur se razjasni โ†’ **IGRA SE ZAฤŒNE** + +--- + +### ๐Ÿ๏ธ AKT 1: PREลฝIVETJE (Demo โ€” Dan 1โ€“200) + +**Cilj:** Preลพivi 200 dni โ†’ odkleni Fazo 1 + +**Kaj se dogaja:** +Kai sam na otoku. Gradi, kmetuje, zbira material. Vsak dan je boj. +Voda je samo iz deลพja (Rain Catcher). Zombiji napadajo ponoฤi. + +#### Zgodba teฤe skozi 3 kanale: + +**1. DNEVNIK (Kai-ev notranji monolog)** +Vsak dan ob prvem pritisku na Sleep: +``` +DAN 1: "Konฤno... Nov dom. Majhna kmetija, ampak dovolj za zaฤetek." +DAN 2: "Danes sem sreฤal zombija. Ime sem mu dal Gronk. (Faza 1 unlock)" +DAN 3: "Flashback... Vidim mamo, oฤeta, Ano... Boli. Ampak moram naprej." +DAN 7: "Teden dni sam. Twin Bond signal je bil ลกibak nocoj." +DAN 30: "En mesec. Preลพivim. Kmetija raste. Ampak Ana... KJE SI?" +DAN 100:"Sto dni. Pol poti. Zbral sem dovolj za... Moram naprej." +DAN 200:"Dvesto dni. Naredil sem, kar sem si obljubil. Zdaj se zaฤne pravi boj." +``` + +**2. TWIN BOND SIGNALI** (nakljuฤno 1ร— na dan, ~10% chance) +``` +[ล ibek telepatski glas] +Ana: "Kai... ฤutiลก me? Sem ujeta... ampak ลฝIVIM. Ne obupaj!" +Ana: "Ne obupaj... Twin Bond... deluje..." +Ana: "Troll me ฤuva... ampak nisem sama..." +Ana: "Kai... najdi me..." +[Signal izgubljen] +``` +Visual: Vijoliฤni blisk na zaslonu, Kai zamrzne za sekundo, nato nadaljuje. + +**3. FLASHBACK SPOMINI** (trigger: pristopi k posebnim predmetom) + +| Predmet | Spomin | +|---------|--------| +| Star foto na tleh | "Sreฤni ฤŒasi" โ€” Ana z metuljem v roki | +| Testna epruveta | "Oฤetov Laboratorij" โ€” "Nikoli ne dotikaj teh vzorฤkov" | +| Stara sveฤa | "Zadnja Veฤerja" โ€” Anin rojstni dan, torta, smeh | + +Vsak flashback = blago screen blur โ†’ Polaroid naredi "klik" โ†’ glas โ†’ nazaj. + +--- + +### ๐ŸŒซ๏ธ AKT 2: FAZA 1 โ€” ODPRTJE (Dan 200+) + +**Trigger:** 200 dni preลพivetih โ†’ fog of war se zaฤne odpirati + +**Unlock:** Gronk (ali prvih 20 kupcev ga dobijo takoj) + +``` +[Fog se delno dvigne] +SISTEM: "Megla se dviga... Svet je veฤji kot si mislil." + +KAI (misli): "Kaj je tam zunaj...?" + +[Gronk se pojavi] +GRONK: "Gronk... pomaga ลกefu?" +KAI: "Ti si... Gronk. Moj pomoฤnik." +GRONK: "Gronk... dober zombi?" +KAI: "Najboljลกi zombi." +GRONK: *Sreฤno mrmra* +``` + +**Nove zgodbe:** +- Scout Zombi razkrije ruลกevine v fogu +- Garaลพa, Rastlinjak, Hlev โ€” vsaka ruลกevina ima **izgubo/zgodovino** (napisi v ruinah) +- Garaลพa: "Tukaj je imel nekdo sanje... Popravi jih." +- Rudnik: "Globoko pod tlemi leลพi odgovor..." + +**Gronk Dialogi** (sproลพi se med delom): +``` +[Med kmetovanjem] +GRONK: "Gronk... sadi... rastline..." +KAI: "Dobro delo, Gronk!" +GRONK: "Gronk... je... ponosen!" + +[Kai je ลพalosten] +GRONK: "ล ef... ลพalosten?" +KAI: "Moja sestra... Pogreลกam jo..." +GRONK: "Gronk... pomagal... najti sestro!" +KAI: "Hvala, Gronk. Res si dober prijatelj." + +[Ko sreฤa Kai ob ognju zveฤer] +GRONK: "Gronk... rad... ogenj. Toplo." +KAI: "Jaz pa rad... mislim na Ano." +GRONK: "Ana... dobra? Kot ลกef?" +KAI: "Boljลกa. Ona je genialna." + +[Grozno vreme] +GRONK: "Gronk... ne mara... mokro." +KAI: "Nobeden ne mara deลพja, Gronk." +GRONK: "Gronk... ljubi... Gronk laลพje govori to." +KAI: ["..."smeh] +``` + +**Parental Ghost (Oltar event):** +Kai zgradi oltar (20 Kamen + 10 Les + 2 Sveฤi) in najde predmete: +- **Inลพenirska ura** (Markov predmet) โ†’ Marko se pojavi kot duh +- **Medaljon** (Elenin predmet) โ†’ Elena se pojavi kot duh + +``` +[Marko Duh โ€” moder svit] +MARKO: "Kai... Sin..." +KAI: "Ata? Ali si... res ti?" +MARKO: "Del mene ostaja. Dokler se spomniลก." +MARKO: "Utri ograjo, sin. Zombiji so ลกibki, ampak horda..." +BUFF AKTIVEN: "Oฤetova Modrost: +15% Craft hitrost" + +[Elena Duh โ€” zlat svit] +ELENA: "Moj Kai... kako si zrasel." +KAI: "Mama... ลฝal mi je. Nisem mogelโ€”" +ELENA: "Molฤi. Nisi kriv. Nikoli nisi bil kriv." +ELENA: "Rastline potrebujejo vodo, Kai. Kot ti ljubezen." +BUFF AKTIVEN: "Mamina Ljubezen: +10% Energy regen na kmetiji" +``` + +--- + +### ๐Ÿ’œ AKT 3: REUNIJA (Faza 2+ โ€” prihodnost) + +**Lokacija:** ฤŒernobil Reaktor (Level 61โ€“75) + +Kai pride skozi radiation zones, premaga Giant Troll King (Phase 1)... + +``` +[Kai vstopi v celico] +[Ana se obrne] +[Njune oฤi se sreฤajo] + +ANA: "...Kai?" +KAI: "Ana... I found you." + +[Teฤeta drug k drugemu, objameta se, joฤeta] + +ANA: "You... you came for me..." +KAI: "Always. Twin Bond, remember?" +ANA: "I felt you. Every day. Searching..." +KAI: "Every single day." + +[Twin Bond FULLY AWAKENS โ€” zlata svetloba] +``` + +**ANA SE PRIDRUลฝI** โ€” postane playable character. Unlock vseh Twin Bond abilityjev: +- Twin Telepathy (govorita brez distance) +- Twin Strike (skupni napad, 2ร— damage) +- Twin Shield (prenos ลกkode med njima) +- Twin Sense (vidita skozi zidove) +- Shared Buffs (hrana/potions za oba) +- Resurrection Ultimate (vsak reลกi drugega 1ร—/dan) + +--- + +### โš–๏ธ AKT 4: ZADNJA IZBIRA (Faza 3 โ€” End Game) + +Dr. Krniฤ‡: *"Pridi v Reactor Core. Ali spusti SUPER VIRUS. Imaลก 7 dni."* + +Po Boss Rush (Dr. Krniฤ‡ + Giant Troll King Phase 2): + +``` +[Cure Machine] +Dr. Krniฤ‡ (dying): "Edini naฤin... Alpha Hybrid Sacrifice. + Eden od vaju mora umreti... da se svet pozdravili." + +[Twin Bond: "Skupaj... Premagava?"] +``` + +**CHOICE SCREEN:** +``` +[ A ] Kai se ลพrtvuje โ†’ Ending 1 +[ B ] Ana se ลพrtvuje โ†’ Ending 2 +[ C ] Zavrni โ†’ Ending 3 (Dark World) +[ D ] Alternativa* โ†’ Ending 4 PERFECT โœจ + * Samo ฤe si zbral VSEH 50 Aninih clues + 9 Key Fragmentov +``` + +--- + +## ๐Ÿ 4 KONCI + +### Ending 1 โ€” ๐Ÿ’” Kai se ลพrtvuje +Kai: *"Ana... ti si genialna. Svet te potrebuje bolj. ลฝivi. Za naju oba."* +Ana: Vodi novi svet. Ima hฤer po imenu **Kai**. + +### Ending 2 โ€” ๐Ÿ’” Ana se ลพrtvuje +Ana: *"Kai... ti si reลกil mene. Pusti meni reลกiti ostale."* +Kai: Vodi novi svet. Ima hฤer po imenu **Ana**. + +### Ending 3 โ€” ๐Ÿ’€ Dark World +Oba zavrneta. Zombiji ostanejo. ลฝivita skupaj na kmetiji veฤno. +*"Ne ลพalim niฤesar."* โ€” Kai (68) + +### Ending 4 โ€” ๐ŸŒŸ Perfect โœจ +Ana: *"Poฤakaj! Twin Bond energija + 50 clues + 9 Fragmentov = ALTERNATIVNA REล ITEV!"* +Oba preลพivita. Svet ozdravljen. Zombiji postanejo *prijazni delavci*. +Zadnja scena: Piknik na kmetiji. Kai, Ana, otroci, smeh. + +--- + +## ๐Ÿ› ๏ธ KAKO SE ZGODBA GRADI V IGRI + +### Sistem 1: IntroScene.js +``` +ASSETS: polaroid_01.png โ€“ polaroid_20.png +CODE: IntroScene.js +TRIGGER: Samo 1x, ob prvem startu +TRACKING: localStorage.setItem('introSeen', true) +``` + +### Sistem 2: Journal (Dnevnik) +``` +ASSETS: ui_dialog.png (mรกr imamo!) +CODE: JournalSystem.js (ustvari) +TRIGGER: Vsak veฤer ob kliku Sleep +DATA: Array dialogov indexiran po dnevu +``` + +### Sistem 3: Twin Bond Signal +``` +ASSETS: vijoliฤno overlay (Graphics โ€” brez asseta) +CODE: TwinBondSystem.js +TRIGGER: Vsak dan, 10% chance ob polnoฤi +SOUND: Ana glas (Edge-TTS proceduralan) +``` + +### Sistem 4: Flashback Memories +``` +ASSETS: 3 predmeti: foto, epruveta, sveฤa +CODE: MemorySystem.js +TRIGGER: Kai stopi v 80px bliลพino predmeta +EFFECT: Screen blur โ†’ Polaroid click โ†’ glas โ†’ blur ven +``` + +### Sistem 5: Gronk Dialogi (Faza 1) +``` +ASSETS: gronk_walk_sheet.png (ลพe imamo!) +CODE: GronjSystem.js (dodamo k NPC logic) +TRIGGER: Triggeri: ob kmetovanju, po 20+ minutah, ob ognju, jokanju +``` + +### Sistem 6: Parental Ghosts (Oltar) +``` +ASSETS: oltar_obj.png, inzenirska_ura.png, medaljon.png +CODE: AltarSystem.js +TRIGGER: Zgradi oltar + najdi oba predmeta +EFFECT: Duh se pojavi, dรก buff, ostane permanent +``` + +--- + +## ๐Ÿ“‹ IMPLEMENTACIJSKI VRSTNI RED + +### ๐ŸŸข SPRINT A โ€” Prolog (Intro Sekvenca) +``` +1. Generiramo 20 Polaroid slik (AI) +2. Napiลกemo IntroScene.js + - Fade in/out med slikami + - Typewriter tekst na vsaki + - ESC/Space skip za vsako +3. Integriramo v game.js: + scene: [IntroScene, GrassScene, UIScene] +4. localStorage tracking โ†’ ne ponavlja se +``` + +### ๐ŸŸก SPRINT B โ€” Daily Journal +``` +1. Napiลกemo dialogArray.js (vse Kai dnevniลกke vnose po dnevu) +2. JournalSystem.js โ†’ ob kliku "Sleep" prikaลพe dialog +3. UIScene: dialog box za prikaz (dialog_panel.png โ€” ลพe imamo!) +``` + +### ๐ŸŸ  SPRINT C โ€” Twin Bond +``` +1. TwinBondSystem.js + - Vsak dan, 10% chance Signal + - Ana voice line (typewriter) + - Vijoliฤni blisk efekt + - UI: Vijoliฤna ikona v HUD blisne +2. HUD: Dodamo Twin Bond ikono (majhna vijoliฤna srce ikona) +``` + +### ๐Ÿ”ต SPRINT D โ€” Flashback Memories +``` +1. Postavimo 3 predmete na otok (foto, epruveta, sveฤa) +2. MemorySystem.js โ€” proximity trigger (80px) +3. Efekt: Phaser camera.setPostPipeline blur +4. Polaroid click zvok (proceduralen) +``` + +--- + +## ๐Ÿ“Š ZGODBA STATUS (2026-03-03) + +``` +PROLOG (20 polaroidov): โ–ˆโ–ˆโ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘ 10% (napisi OK, slike manjkajo) +KAI DNEVNIK: โ–ˆโ–ˆโ–ˆโ–ˆโ–‘โ–‘โ–‘โ–‘โ–‘โ–‘ 40% (tekst pisan, kode ni) +TWIN BOND SIGNALI: โ–ˆโ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘ 5% (dizajn ok, implementacija 0) +FLASHBACK MEMORIES: โ–ˆโ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘ 5% (dizajn ok, predmeti manjkajo) +GRONK DIALOGI: โ–ˆโ–ˆโ–ˆโ–ˆโ–‘โ–‘โ–‘โ–‘โ–‘โ–‘ 40% (tekst pisan, Gronk ni v igri) +PARENTAL GHOSTS: โ–ˆโ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘ 5% (samo dizajn) +AKT 2-4 (Faza 1+): โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘ 0% (prihodnost) +``` + +--- + +*Posodobljeno: 2026-03-03 ob 13:19* +*Za dialoge: `DEMO_FAZA1_NAPISI_DIALOGI.md`* +*Za full lore: `ZGODBA_CELOTNA.md`* diff --git a/nova farma TRAE/src/game.js b/nova farma TRAE/src/game.js index e0ba933d9..cf95d526c 100644 --- a/nova farma TRAE/src/game.js +++ b/nova farma TRAE/src/game.js @@ -3,6 +3,7 @@ if (typeof process !== 'undefined' && process.env) { process.env.ELECTRON_DISABLE_SECURITY_WARNINGS = 'true'; } +import MenuScene from './scenes/MenuScene.js'; import GrassScene from './scenes/GrassScene_Clean.js'; import UIScene from './scenes/UIScene.js'; @@ -14,8 +15,8 @@ const config = { mode: Phaser.Scale.FIT, autoCenter: Phaser.Scale.CENTER_BOTH }, - pixelArt: true, // Zagotovi ostrino pri zumiranju (pixel art style) - backgroundColor: '#1a1a1a', // Temno siva, da slika izstopa + pixelArt: false, // Meni uporablja smooth slike + backgroundColor: '#000000', parent: 'body', physics: { default: 'arcade', @@ -24,7 +25,9 @@ const config = { gravity: { y: 0 } } }, - scene: [GrassScene, UIScene] + // Vrstni red: MenuScene je prva (avtomatski start), ostale so pasivne + scene: [MenuScene, GrassScene, UIScene] }; const game = new Phaser.Game(config); + diff --git a/nova farma TRAE/src/scenes/GrassScene_Clean.js b/nova farma TRAE/src/scenes/GrassScene_Clean.js index 4dc5c65d9..9de40e470 100644 --- a/nova farma TRAE/src/scenes/GrassScene_Clean.js +++ b/nova farma TRAE/src/scenes/GrassScene_Clean.js @@ -1,4 +1,9 @@ import BuildingSystem from '../systems/BuildingSystem.js'; +import FarmingSystem from '../systems/FarmingSystem.js'; +import WaterSystem from '../systems/WaterSystem.js'; +import DayNightSystem from '../systems/DayNightSystem.js'; +import InventorySystem from '../systems/InventorySystem.js'; +import ZombieSystem from '../systems/ZombieSystem.js'; export default class GrassSceneClean extends Phaser.Scene { constructor() { @@ -123,6 +128,15 @@ export default class GrassSceneClean extends Phaser.Scene { // 8. Weather System this.load.image('rain_drops', 'DEMO_FAZA1/Environment/rain_drops.png'); + + // 9. Farming System Assets + FarmingSystem.preload(this); + + // 10. Water System Assets + WaterSystem.preload(this); + + // 11. Zombie System Assets + ZombieSystem.preload(this); } // =================================================================== @@ -1091,6 +1105,10 @@ export default class GrassSceneClean extends Phaser.Scene { console.log('๐Ÿ—๏ธ Building System initialized โ€” press [B] to open build mode'); + // === ZAลฝENIMO UIScene kot overlay (HUD sloj) === + if (!this.scene.isActive('UIScene')) { + this.scene.launch('UIScene'); + } // --- ANIMATIONS --- // 0-3: Down, 4-7: Left, 8-11: Right, 12-15: Up @@ -1212,6 +1230,147 @@ export default class GrassSceneClean extends Phaser.Scene { console.log('๐ŸŒง๏ธ Rain system initialized (procedural canvas drops)!'); + + // โ”€โ”€โ”€ FARMING SYSTEM โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + this.farmingSystem = new FarmingSystem(this, { + islandX: this.islandX, + islandY: this.islandY, + tileSize: this.TILE_SIZE, + }); + + // Harvest event โ†’ pokaลพi gold gain in posodobi inventory + this.events.on('harvest', (data) => { + console.log(`๐ŸŒพ Harvest: ${data.item} ร—${data.count} = ${data.value}g`); + // Posodobi inventory gold (ko bo InventorySystem) + if (this.playerGold !== undefined) this.playerGold += data.value; + else this.playerGold = data.value; + // Emit do UIScene + const uiScene = this.scene.get('UIScene'); + if (uiScene && uiScene.events) { + uiScene.events.emit('goldChanged', this.playerGold); + } + }); + + console.log('โœ… FarmingSystem ready โ€” [E] ore, posadi, zalij, pozeni!'); + + // โ”€โ”€โ”€ WATER SYSTEM โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + this.waterSystem = new WaterSystem(this, { + tileSize: this.TILE_SIZE, + }); + + // Postavi Rain Catcher blizu centra otoka (kamor Kai spawn-a) + const rcX = this.islandX + this.islandWidth / 2 + 200; + const rcY = this.islandY + this.islandHeight / 2; + this.waterSystem.placeRainCatcher(rcX, rcY); + + // Poveลพi water system z farming sistemom (preverj vodo pred zalivanjem) + this.farmingSystem.waterSystem = this.waterSystem; + + // WATER CHANGED EVENT โ†’ UI update + this.events.on('waterChanged', (data) => { + const uiScene = this.scene.get('UIScene'); + if (uiScene && uiScene.events) { + uiScene.events.emit('waterChanged', data); + } + }); + + console.log('โœ… WaterSystem ready โ€” [F] napolni vedro pri Rain Catcher-ju!'); + + // โ”€โ”€โ”€ DAY/NIGHT SYSTEM โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + this.dayNightSystem = new DayNightSystem(this, { + speed: 1, // 1 sec real = 1 min igre (24 min = 1 dan) + }); + + // === EVENT WIRE-UP === + + // Dan se je spremenil โ†’ farma tick + this.events.on('dayChanged', ({ day }) => { + if (this.farmingSystem) this.farmingSystem.onNewDay(day); + console.log(`๐Ÿ“… Dan ${day} priลกel!`); + }); + + // Deลพ โ†’ Rain Catcher zbira + this.events.on('rainDay', ({ day }) => { + if (this.waterSystem) this.waterSystem.onRain(); + this.triggerRainfall(); // Vizualni deลพ + console.log(`๐ŸŒง๏ธ Deลพen dan ${day}`); + }); + + // Jutro โ†’ Rast trave/dreves + this.events.on('morningArrived', () => { + this.morningGrowthCheck(); + }); + + // Ur a sprememba โ†’ UIScene clock + this.events.on('timeChanged', (data) => { + const uiScene = this.scene.get('UIScene'); + if (uiScene && uiScene.events) { + uiScene.events.emit('timeChanged', data); + } + }); + + // Registriraj spalno vreฤo kot sleep objekt + // (spawn point je center otoka) + 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!'); + + // โ”€โ”€โ”€ INVENTORY SYSTEM โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + this.inventorySystem = new InventorySystem(this); + + // Poลกlji zaฤetni hotbar stanje v UIScene + this.scene.get('UIScene')?.events.emit('hotbarUpdate', { + slots: this.inventorySystem.hotbar.map((key, i) => { + const def = this.inventorySystem.ITEM_DEFS[key]; + return key ? { + key, icon: def?.icon, name: def?.name, + count: this.inventorySystem.getCount(key), + active: i === 0, type: def?.type, + stackable: def?.stackable + } : null; + }), + activeSlot: 0, + gold: 0, + }); + + console.log('โœ… InventorySystem ready โ€” [1-7] hotbar, scroll wheel!'); + + // โ”€โ”€โ”€ ZOMBIE SYSTEM โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + this.zombieSystem = new ZombieSystem(this, { + islandX: this.islandX, + islandY: this.islandY, + islandW: this.islandWidth, + islandH: this.islandHeight, + }); + this.zombieSystem.createAnimations(); + + // Noฤ โ†’ Zaฤni spawnat + this.events.on('nightArrived', () => { + this.zombieSystem.startNightSpawning(); + }); + + // Jutro โ†’ Ustavi spawn (zombiji ostanejo) + this.events.on('morningArrived', () => { + this.zombieSystem.stopNightSpawning(); + }); + + // Zombi udomaฤen event + this.events.on('zombieTamed', ({ zombie }) => { + console.log('โœจ Kai je udomaฤil zombija!'); + }); + + console.log('โœ… ZombieSystem ready โ€” [SPACE] za Alfa Moฤ!'); + + // === TAKOJ SPAWNI ZOMBIJA (za testiranje, podnevi+ponoฤi) === + this.time.delayedCall(3000, () => { + if (this.zombieSystem) { + this.zombieSystem.spawnZombie(); // 1x takoj ob startu + console.log('๐ŸงŸ Test zombi spawnan!'); + } + }); + } @@ -1703,6 +1862,26 @@ export default class GrassSceneClean extends Phaser.Scene { } } + // === WATER SYSTEM UPDATE === + if (this.waterSystem) { + this.waterSystem.update(this.kai); + } + + // === DAY/NIGHT SYSTEM UPDATE === + if (this.dayNightSystem) { + this.dayNightSystem.update(delta, this.kai); + } + + // === ZOMBIE SYSTEM UPDATE === + if (this.zombieSystem) { + this.zombieSystem.update(this.kai, delta); + } + + // === FARMING SYSTEM UPDATE === + if (this.farmingSystem) { + this.farmingSystem.update(this.kai, time); + } + // === BUILDING SYSTEM: Ghost follows pointer, building Y-sorting === if (this.buildingSystem) { this.buildingSystem.update(this.input.activePointer); diff --git a/nova farma TRAE/src/scenes/MenuScene.js b/nova farma TRAE/src/scenes/MenuScene.js new file mode 100644 index 000000000..fb9890d78 --- /dev/null +++ b/nova farma TRAE/src/scenes/MenuScene.js @@ -0,0 +1,359 @@ +/** + * ============================================================ + * MenuScene.js โ€” Nova Farma | Glavni Meni + * ============================================================ + * Vsebuje: + * - Animirano ozadje (otok ponoฤi) + * - Naslov igre z glow efektom + * - Kai lik (lebdi + diha) + * - Gumbi: IGRAJ, NASTAVITVE, IZHOD + * - Trial Version badge + Purchase badge + * - Firefly particle efekt + * - Fog (megla) ki se premika + * ============================================================ + */ +export default class MenuScene extends Phaser.Scene { + constructor() { + super({ key: 'MenuScene' }); + } + + preload() { + this.load.path = 'assets/'; + + // Ozadje + this.load.image('menu_bg', 'DEMO_FAZA1/UI/menu_background.png'); + + // Liki + this.load.image('kai_menu', 'DEMO_FAZA1/Characters/Kai_Dreads.png'); + + // UI Badges + this.load.image('badge_trial', 'DEMO_FAZA1/UI/badge_trial.png'); + this.load.image('badge_buy', 'DEMO_FAZA1/UI/badge_purchase.png'); + + // Gumb ozadje (dialog panel za gumbe) + this.load.image('ui_dialog', 'DEMO_FAZA1/UI/dialog_panel.png'); + } + + create() { + const W = this.cameras.main.width; // 1920 + const H = this.cameras.main.height; // 1080 + + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + // OZADJE + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + this.bg = this.add.image(W / 2, H / 2, 'menu_bg') + .setDisplaySize(W, H) + .setDepth(0); + + // Temna vinjeta (robovi temnejลกi) + const vignette = this.add.graphics().setDepth(1); + const vigRad = this.add.graphics().setDepth(1); + // Gradientni overlay โ€” temno rob, svetel center + for (let i = 0; i < 8; i++) { + const alpha = 0.04 * (8 - i); + vignette.fillStyle(0x000000, alpha); + vignette.fillRect(0, 0, W * (i * 0.06), H); + vignette.fillRect(W - W * (i * 0.06), 0, W * (i * 0.06), H); + vignette.fillRect(0, 0, W, H * (i * 0.04)); + vignette.fillRect(0, H - H * (i * 0.04), W, H * (i * 0.04)); + } + + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + // FIREFLIES โ€” lebdeฤe svetlobne pike + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + this.fireflies = []; + for (let i = 0; i < 35; i++) { + const ff = { + x: Phaser.Math.Between(60, W - 60), + y: Phaser.Math.Between(100, H - 150), + r: Phaser.Math.FloatBetween(2, 5), + alpha: Phaser.Math.FloatBetween(0.2, 0.9), + speed: Phaser.Math.FloatBetween(0.3, 0.9), + offset: Math.random() * Math.PI * 2, + color: Phaser.Math.RND.pick([0x44ffcc, 0xaaffaa, 0x88ffdd, 0xccff66]), + }; + this.fireflies.push(ff); + } + this.fireflyGfx = this.add.graphics().setDepth(2); + + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + // KAI โ€” desno, lebdeฤ + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + this.kaiImg = this.add.image(W * 0.78, H * 0.62, 'kai_menu') + .setScale(0.95) + .setOrigin(0.5, 1.0) + .setDepth(10); + + // Kai lebdi gor/dol + this.tweens.add({ + targets: this.kaiImg, + y: H * 0.62 - 18, + duration: 2200, + yoyo: true, + repeat: -1, + ease: 'Sine.easeInOut', + }); + + // Subtilen glow pod Kai-em (shadow/aura) + this.kaiAura = this.add.ellipse(W * 0.78, H * 0.625, 180, 30, 0x44ffcc, 0.18) + .setDepth(9); + this.tweens.add({ + targets: this.kaiAura, + scaleX: { from: 0.85, to: 1.15 }, + alpha: { from: 0.18, to: 0.08 }, + duration: 2200, + yoyo: true, + repeat: -1, + ease: 'Sine.easeInOut', + }); + + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + // NASLOV IGRE + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + // Subtitle zgoraj + this.add.text(W * 0.32, H * 0.12, 'KRVAVA ลฝETEV', { + fontFamily: '"Georgia", serif', + fontSize: '28px', + color: '#cc6666', + letterSpacing: 12, + stroke: '#000000', + strokeThickness: 4, + }).setOrigin(0.5).setDepth(10).setAlpha(0.85); + + // Glavni naslov + const title = this.add.text(W * 0.32, H * 0.22, 'NOVA\nFARMA', { + fontFamily: '"Georgia", serif', + fontSize: '130px', + fontStyle: 'bold', + color: '#e8d5a3', + stroke: '#1a0a00', + strokeThickness: 14, + lineSpacing: -10, + align: 'center', + }).setOrigin(0.5).setDepth(10); + + // Glow pulse na naslovu + this.tweens.add({ + targets: title, + alpha: { from: 1.0, to: 0.85 }, + duration: 2800, + yoyo: true, + repeat: -1, + ease: 'Sine.easeInOut', + }); + + // Tagline pod naslovom + this.add.text(W * 0.32, H * 0.41, '"Preลพivi. Najdi jo. Ne pozabi."', { + fontFamily: '"Georgia", serif', + fontStyle: 'italic', + fontSize: '22px', + color: '#8899aa', + stroke: '#000000', + strokeThickness: 3, + }).setOrigin(0.5).setDepth(10); + + // Horizontal divider + const div = this.add.graphics().setDepth(10); + div.lineStyle(1, 0x44ffcc, 0.35); + div.lineBetween(W * 0.12, H * 0.45, W * 0.52, H * 0.45); + + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + // GUMBI + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + const BTN_X = W * 0.32; + const BTN_Y_START = H * 0.54; + const BTN_GAP = 95; + + const buttons = [ + { label: 'โ–ถ IGRAJ', id: 'play', color: '#44ffcc', hColor: '#ffffff' }, + { label: 'โš™ NASTAVITVE', id: 'settings', color: '#aabbcc', hColor: '#ffffff' }, + { label: 'โœ• IZHOD', id: 'quit', color: '#cc6666', hColor: '#ff8888' }, + ]; + + this._btns = []; + + buttons.forEach((b, i) => { + const y = BTN_Y_START + i * BTN_GAP; + + // Gumb ozadje (Graphics) + const bg = this.add.graphics().setDepth(11); + const drawBg = (hover) => { + bg.clear(); + bg.fillStyle(hover ? 0x1a2a20 : 0x0d1510, hover ? 0.95 : 0.80); + bg.fillRoundedRect(BTN_X - 190, y - 28, 380, 56, 12); + bg.lineStyle(hover ? 2 : 1, hover ? 0x44ffcc : 0x224433, hover ? 1.0 : 0.5); + bg.strokeRoundedRect(BTN_X - 190, y - 28, 380, 56, 12); + }; + drawBg(false); + + // Gumb tekst + const txt = this.add.text(BTN_X, y, b.label, { + fontFamily: '"Arial Black", Arial', + fontSize: '26px', + color: b.color, + stroke: '#000000', + strokeThickness: 4, + letterSpacing: 3, + }).setOrigin(0.5).setDepth(12); + + // Hit zone + const zone = this.add.zone(BTN_X, y, 380, 56) + .setDepth(13) + .setInteractive({ cursor: 'pointer' }); + + zone.on('pointerover', () => { + drawBg(true); + txt.setColor(b.hColor).setScale(1.05); + // Puลกฤica + this.tweens.add({ targets: txt, x: BTN_X + 8, duration: 120, ease: 'Quad.out' }); + }); + zone.on('pointerout', () => { + drawBg(false); + txt.setColor(b.color).setScale(1.0); + this.tweens.add({ targets: txt, x: BTN_X, duration: 120, ease: 'Quad.out' }); + }); + zone.on('pointerdown', () => this._onButton(b.id)); + + this._btns.push({ bg, txt, zone }); + }); + + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + // TRIAL BADGE (levo zgoraj) + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + const trialBadge = this.add.image(30, 22, 'badge_trial') + .setOrigin(0, 0) + .setScale(0.32) + .setDepth(20); + + this.tweens.add({ + targets: trialBadge, + y: 28, + duration: 1600, + yoyo: true, + repeat: -1, + ease: 'Sine.easeInOut', + }); + + // Purchase badge (desno spodaj, majhen) + this.add.image(W - 20, H - 20, 'badge_buy') + .setOrigin(1, 1) + .setScale(0.28) + .setDepth(20) + .setInteractive({ cursor: 'pointer' }) + .on('pointerover', function () { this.setScale(0.31); }) + .on('pointerout', function () { this.setScale(0.28); }); + + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + // VERZIJA + AVTOR (spodaj levo) + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + this.add.text(20, H - 20, 'v0.1.0-DEMO | ยฉ 2026 HipoDevil666', { + fontFamily: 'Arial', + fontSize: '14px', + color: '#445544', + }).setOrigin(0, 1).setDepth(20); + + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + // CONTROLS HINT (spodaj desno) + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + this.add.text(W - 20, H - 20, 'WASD Gibanje | E Interakcija | SPACE Alfa Moฤ', { + fontFamily: 'Arial', + fontSize: '14px', + color: '#334433', + }).setOrigin(1, 1).setDepth(20); + + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + // FADE IN ob vstopu + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + this.cameras.main.fadeIn(800, 0, 0, 0); + + // Akumuliraj ฤas za animacije + this._t = 0; + } + + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + // GUMB LOGIKA + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + _onButton(id) { + if (id === 'play') { + this.cameras.main.fadeOut(600, 0, 0, 0); + this.cameras.main.once('camerafadeoutcomplete', () => { + this.scene.start('GrassSceneClean'); + }); + } + else if (id === 'settings') { + // TODO: SettingsScene + this._showToast('Nastavitve kmalu! ๐Ÿ”ง'); + } + else if (id === 'quit') { + // Electron quit + if (typeof window !== 'undefined' && window.electronAPI) { + window.electronAPI.quit(); + } else if (typeof process !== 'undefined') { + window.close(); + } + } + } + + // Kratko sporoฤilo na zaslonu + _showToast(msg) { + const W = this.cameras.main.width; + const toast = this.add.text(W / 2, 80, msg, { + fontFamily: 'Arial', + fontSize: '22px', + color: '#44ffcc', + stroke: '#000', + strokeThickness: 3, + backgroundColor: '#0a1a10cc', + padding: { x: 18, y: 8 }, + }).setOrigin(0.5).setDepth(100).setAlpha(0); + + this.tweens.add({ + targets: toast, + alpha: { from: 0, to: 1 }, + y: 70, + duration: 300, + ease: 'Quad.out', + onComplete: () => { + this.time.delayedCall(1400, () => { + this.tweens.add({ + targets: toast, + alpha: 0, + duration: 400, + onComplete: () => toast.destroy(), + }); + }); + } + }); + } + + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + // UPDATE + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + update(time, delta) { + this._t += delta / 1000; + + // Animiraj fireflye + this.fireflyGfx.clear(); + for (const ff of this.fireflies) { + const pulse = 0.5 + 0.5 * Math.sin(this._t * ff.speed + ff.offset); + const a = 0.1 + pulse * 0.8; + const r = ff.r * (0.7 + 0.3 * pulse); + + // Premik (leno lebdenje) + ff.x += Math.sin(this._t * 0.3 + ff.offset) * 0.4; + ff.y += Math.cos(this._t * 0.25 + ff.offset) * 0.3; + + // Wrap + if (ff.x < 20) ff.x = this.cameras.main.width - 20; + if (ff.x > this.cameras.main.width - 20) ff.x = 20; + + // Glow halo + this.fireflyGfx.fillStyle(ff.color, a * 0.15); + this.fireflyGfx.fillCircle(ff.x, ff.y, r * 3.5); + + // Jedro + this.fireflyGfx.fillStyle(ff.color, a); + this.fireflyGfx.fillCircle(ff.x, ff.y, r); + } + } +} diff --git a/nova farma TRAE/src/scenes/UIScene.js b/nova farma TRAE/src/scenes/UIScene.js index 989213c40..01c508f01 100644 --- a/nova farma TRAE/src/scenes/UIScene.js +++ b/nova farma TRAE/src/scenes/UIScene.js @@ -1,49 +1,646 @@ +/** + * ============================================================= + * UIScene.js โ€” Nova Farma HUD Layer + * ============================================================= + * Overlay scene (runs on top of GrassScene_Clean) + * Contains ALL UI elements: + * + * TOP LEFT: Trial Version badge + HP bar + energy bar + * TOP RIGHT: Weather widget (ura + vreme) + * BOT LEFT: Dan/Ura text + * BOT CENTER: Hotbar (7 slotov) + action button + * BOT RIGHT: Minimap compass ring + * POPUP: Inventory panel [I] toggle + * OVERLAY: Health Critical pulse (pri < 25% HP) + * ============================================================= + */ export default class UIScene extends Phaser.Scene { constructor() { - super({ key: 'UIScene', active: false }); + super({ key: 'UIScene', active: false }); // Zaลพenemo roฤno iz GrassScene + + // Stanje igre (posodablja se iz GrassScene prek eventos) + this.gameState = { + hp: 100, + maxHp: 100, + energy: 100, + maxEnergy: 100, + day: 1, + hour: 6, + minute: 0, + weather: 'rain', // 'sun', 'rain', 'storm', 'cloud' + inventoryOpen: false, + hotbarItems: [null, null, null, null, null, null, null], // 7 slotov + activeSlot: 0, + }; } preload() { this.load.path = 'assets/'; - // Load UI Assets here (moved from GrassScene) + + // โ”€โ”€ Vse UI assete naloลพimo tukaj โ”€โ”€ this.load.image('ui_health_bar', 'DEMO_FAZA1/UI/health_bar.png'); - this.load.image('ui_weather_widget', 'DEMO_FAZA1/UI/weather_widget.png'); + this.load.image('ui_health_critical', 'DEMO_FAZA1/UI/health_critical.png'); + this.load.image('ui_weather', 'DEMO_FAZA1/UI/weather_widget.png'); this.load.image('ui_minimap', 'DEMO_FAZA1/UI/minimap_frame.png'); this.load.image('ui_hotbar', 'DEMO_FAZA1/UI/hotbar_background.png'); + this.load.image('ui_slot', 'DEMO_FAZA1/UI/inventory_slot.png'); this.load.image('ui_action_btn', 'DEMO_FAZA1/UI/action_btn.png'); this.load.image('ui_badge_trial', 'DEMO_FAZA1/UI/badge_trial.png'); + this.load.image('ui_badge_buy', 'DEMO_FAZA1/UI/badge_purchase.png'); + this.load.image('ui_inventory', 'DEMO_FAZA1/UI/inventory_panel.png'); + this.load.image('ui_dialog', 'DEMO_FAZA1/UI/dialog_panel.png'); } create() { - // Simple UI Layout - const width = this.cameras.main.width; - const height = this.cameras.main.height; - const PAD = 60; // Increased padding for better visibility + const W = this.cameras.main.width; // 1920 + const H = this.cameras.main.height; // 1080 + const PAD = 24; - // 0. TRIAL BADGE (Top Center - The "Hook") - this.add.image(width / 2, PAD, 'ui_badge_trial') - .setOrigin(0.5, 0) - .setScale(1.0) // Increased scale - .setDepth(100); // Ensure it's on top + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + // TOP LEFT โ€” Trial badge + HP + Energy + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - // 1. Health (Top Left) - let health = this.add.image(PAD, PAD, 'ui_health_bar').setOrigin(0, 0).setScale(1.0); + // Trial Version Badge + this.trialBadge = this.add.image(PAD, PAD, 'ui_badge_trial') + .setOrigin(0, 0) + .setScale(0.38) + .setDepth(1000); - // 2. Weather (Top Right) - let weather = this.add.image(width - PAD, PAD, 'ui_weather_widget').setOrigin(1, 0).setScale(1.0); + // Badge lebdi animacija + this.tweens.add({ + targets: this.trialBadge, + y: PAD + 6, + duration: 1800, + yoyo: true, + repeat: -1, + ease: 'Sine.easeInOut', + }); - // 3. Minimap (Bottom Right) - let minimap = this.add.image(width - PAD, height - PAD, 'ui_minimap').setOrigin(1, 1).setScale(0.9); + // โ”€โ”€ HP Bar (health_bar.png je vertikalni triptih: full/empty/empty) โ”€ + // Spritesheet je 3 vrstice: top=HP full, mid=HP empty, bot=Energy + // Uporabljamo health_bar.png kot ozadje in riลกemo fill ฤez njega + const HP_X = PAD + 10; + const HP_Y = PAD + 130; + const HP_W = 420; + const HP_H = 36; - // 4. Hotbar (Bottom Center) - let hotbar = this.add.image(width / 2, height - PAD, 'ui_hotbar').setOrigin(0.5, 1).setScale(1.0); + // Ozadje HP (temno rdeฤe) + this.hpBg = this.add.graphics().setDepth(1001); + this.hpBg.fillStyle(0x4a0000, 1.0); + this.hpBg.fillRoundedRect(HP_X, HP_Y, HP_W, HP_H, 8); - // 5. Action Button (Right of Hotbar) - let action = this.add.image(width / 2 + 400, height - PAD, 'ui_action_btn').setOrigin(0.5, 1).setScale(1.0); + // HP Fill (svetlo rdeฤa) + this.hpFill = this.add.graphics().setDepth(1002); - // Debug Text - this.add.text(width / 2, 50, 'HUD LAYER ACTIVE', { - fontSize: '24px', fill: '#00ff00', stroke: '#000', strokeThickness: 4 - }).setOrigin(0.5); + // HP ikona (skull) โ€“ kratko besedilo label + this.add.text(HP_X - 5, HP_Y + HP_H / 2, 'โค๏ธ', { fontSize: '22px' }) + .setOrigin(1, 0.5).setDepth(1003); + + // HP vrednost text + this.hpText = this.add.text(HP_X + HP_W / 2, HP_Y + HP_H / 2, '100 / 100', { + fontFamily: 'Arial Black', + fontSize: '16px', + color: '#ffffff', + stroke: '#000000', + strokeThickness: 4, + }).setOrigin(0.5, 0.5).setDepth(1004); + + // โ”€โ”€ Energy Bar โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + const EN_X = HP_X; + const EN_Y = HP_Y + HP_H + 8; + const EN_W = HP_W; + const EN_H = 24; + + this.energyBg = this.add.graphics().setDepth(1001); + this.energyBg.fillStyle(0x1a1a00, 1.0); + this.energyBg.fillRoundedRect(EN_X, EN_Y, EN_W, EN_H, 6); + + this.energyFill = this.add.graphics().setDepth(1002); + + this.add.text(EN_X - 5, EN_Y + EN_H / 2, 'โšก', { fontSize: '16px' }) + .setOrigin(1, 0.5).setDepth(1003); + + this.energyText = this.add.text(EN_X + EN_W / 2, EN_Y + EN_H / 2, '100%', { + fontFamily: 'Arial Black', + fontSize: '13px', + color: '#ffee44', + stroke: '#000000', + strokeThickness: 3, + }).setOrigin(0.5, 0.5).setDepth(1004); + + // Shrani pozicije za redraw + this._hpBar = { x: HP_X, y: HP_Y, w: HP_W, h: HP_H }; + this._enBar = { x: EN_X, y: EN_Y, w: EN_W, h: EN_H }; + + // Prviฤ nariลกi + this._redrawBars(); + + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + // TOP RIGHT โ€” Weather Widget (ura + vreme) + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + this.weatherWidget = this.add.image(W - PAD, PAD, 'ui_weather') + .setOrigin(1, 0) + .setScale(0.55) + .setDepth(1000); + + // Ura text (ฤez weather widget) + this.timeText = this.add.text(W - PAD - 62, PAD + 68, '06:00', { + fontFamily: 'Arial Black', + fontSize: '28px', + color: '#ff3333', + stroke: '#000000', + strokeThickness: 4, + }).setOrigin(0.5, 0.5).setDepth(1005); + + // Dan label (pod uro) + this.dayText = this.add.text(W - PAD - 135, PAD + 10, 'DAN 1', { + fontFamily: 'Arial Black', + fontSize: '18px', + color: '#ffdd44', + stroke: '#000000', + strokeThickness: 4, + }).setOrigin(0.5, 0).setDepth(1005); + + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + // BOTTOM CENTER โ€” Hotbar (7 slotov) + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + const HOTBAR_SCALE = 0.72; + this.hotbarBg = this.add.image(W / 2, H - PAD, 'ui_hotbar') + .setOrigin(0.5, 1) + .setScale(HOTBAR_SCALE) + .setDepth(1000); + + // Hotbar dimenzije (hotbar_background.png = ~1120ร—160px) + const HOTBAR_W = 1120 * HOTBAR_SCALE; + const HOTBAR_H = 160 * HOTBAR_SCALE; + const HOT_START_X = W / 2 - HOTBAR_W / 2 + 56; + const HOT_Y = H - PAD - HOTBAR_H / 2; + const SLOT_W = (HOTBAR_W - 112) / 7; + + // Shrani za event listener + this._hotbarW = HOTBAR_W; + this._hotbarH = HOTBAR_H; + this._slotW = SLOT_W; + this._hotY = HOT_Y; + + this.hotbarSlots = []; + for (let i = 0; i < 7; i++) { + const sx = HOT_START_X + i * SLOT_W + SLOT_W / 2; + + // Slot number label + const numLabel = this.add.text(sx - SLOT_W * 0.35, HOT_Y - HOTBAR_H * 0.32, `${i + 1}`, { + fontFamily: 'Arial', + fontSize: '12px', + color: '#888888', + }).setOrigin(0, 0).setDepth(1002); + + // Active highlight + const highlight = this.add.graphics().setDepth(1001); + if (i === this.gameState.activeSlot) { + highlight.lineStyle(3, 0xffdd00, 1.0); + highlight.strokeRoundedRect( + sx - SLOT_W / 2 + 4, HOT_Y - HOTBAR_H / 2 + 4, + SLOT_W - 8, HOTBAR_H - 8, 6 + ); + } + this.hotbarSlots.push({ x: sx, y: HOT_Y, highlight, numLabel }); + } + + // โ”€โ”€ Action Button (desno od hotbara) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + this.actionBtn = this.add.image(W / 2 + HOTBAR_W / 2 + 50, H - PAD - 40, 'ui_action_btn') + .setOrigin(0.5, 0.5) + .setScale(0.9) + .setDepth(1000) + .setInteractive({ cursor: 'pointer' }); + + this.actionBtn.on('pointerover', () => this.actionBtn.setTint(0xffdd88)); + this.actionBtn.on('pointerout', () => this.actionBtn.clearTint()); + + // Crafting label + this.add.text(this.actionBtn.x, H - PAD + 2, 'CRAFTING', { + fontFamily: 'Arial', + fontSize: '13px', + color: '#aaaaaa', + stroke: '#000', + strokeThickness: 2, + }).setOrigin(0.5, 0).setDepth(1002); + + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + // BOTTOM RIGHT โ€” Minimap Compass + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + const MM_SCALE = 0.55; + const MM_SIZE = 256 * MM_SCALE; // compass je 256ร—256 + + this.minimapFrame = this.add.image(W - PAD, H - PAD, 'ui_minimap') + .setOrigin(1, 1) + .setScale(MM_SCALE) + .setDepth(1000); + + // Minimap inner black fill (bg) + this.minimapBg = this.add.graphics().setDepth(999); + const mmCx = W - PAD - MM_SIZE / 2; + const mmCy = H - PAD - MM_SIZE / 2; + this.minimapBg.fillStyle(0x001a33, 0.85); + this.minimapBg.fillCircle(mmCx, mmCy, MM_SIZE / 2 - 12); + + // Kai dot on minimap + this.minimapKaiDot = this.add.graphics().setDepth(1001); + this.minimapKaiDot.fillStyle(0x44ffaa, 1.0); + this.minimapKaiDot.fillCircle(mmCx, mmCy, 5); + + // Store for update + this._mmCx = mmCx; + this._mmCy = mmCy; + this._mmR = MM_SIZE / 2 - 16; + + // "MAP" label + this.add.text(mmCx, mmCy + MM_SIZE / 2 - 8, 'MAPA', { + fontFamily: 'Arial', + fontSize: '11px', + color: '#556655', + }).setOrigin(0.5, 1).setDepth(1002); + + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + // INVENTORY POPUP (I tipka toggle) + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + const INV_SCALE = 0.75; + const INV_W = 600 * INV_SCALE; + const INV_H = 600 * INV_SCALE; + + this.inventoryPanel = this.add.image(W / 2, H / 2, 'ui_inventory') + .setOrigin(0.5, 0.5) + .setScale(INV_SCALE) + .setDepth(5000) + .setVisible(false); + + // Inventory title + this.inventoryTitle = this.add.text(W / 2, H / 2 - INV_H / 2 + 28, 'INVENTAR', { + fontFamily: 'Arial Black', + fontSize: '22px', + color: '#e8d5a3', + stroke: '#000000', + strokeThickness: 4, + }).setOrigin(0.5, 0.5).setDepth(5001).setVisible(false); + + // Close hint + this.inventoryClose = this.add.text(W / 2, H / 2 + INV_H / 2 - 20, '[I] Zapri', { + fontFamily: 'Arial', + fontSize: '16px', + color: '#888888', + }).setOrigin(0.5, 1).setDepth(5001).setVisible(false); + + // Dim overlay when inventory open + this.invDim = this.add.rectangle(W / 2, H / 2, W, H, 0x000000, 0.4) + .setDepth(4999) + .setVisible(false); + + // [I] tipka toggle + this.input.keyboard.on('keydown-I', () => this.toggleInventory()); + + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + // HEALTH CRITICAL OVERLAY (pulse ko HP < 25%) + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + this.criticalOverlay = this.add.image(W / 2, H / 2, 'ui_health_critical') + .setOrigin(0.5, 0.5) + .setScale(Math.max(W / 512, H / 512)) // Pokrije ekran + .setAlpha(0) + .setDepth(4000); + + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + // DIALOG BOX (spodaj, za govor NPCjev) + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + const DLG_W = 900; + const DLG_H = 160; + + this.dialogPanel = this.add.image(W / 2, H - 20, 'ui_dialog') + .setOrigin(0.5, 1) + .setDisplaySize(DLG_W, DLG_H) + .setDepth(6000) + .setVisible(false); + + this.dialogText = this.add.text(W / 2, H - 30, '', { + fontFamily: '"Courier New", monospace', + fontSize: '20px', + color: '#2a1a00', + wordWrap: { width: DLG_W - 80 }, + align: 'left', + }).setOrigin(0.5, 1).setDepth(6001).setVisible(false); + + this.dialogName = this.add.text(W / 2 - DLG_W / 2 + 40, H - DLG_H - 10, '', { + fontFamily: 'Arial Black', + fontSize: '18px', + color: '#ffdd88', + stroke: '#000000', + strokeThickness: 3, + }).setOrigin(0, 1).setDepth(6001).setVisible(false); + + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + // KEYBOARD SHORTCUTS HINT (spodaj levo) + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + this.add.text(PAD, H - PAD, '[I] Inventar [B] Gradnja [ESC] Pavza', { + fontFamily: 'Arial', + fontSize: '14px', + color: '#444444', + stroke: '#000000', + strokeThickness: 2, + }).setOrigin(0, 1).setDepth(1000); + + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + // VZPOSTAVI KOMUNIKACIJO Z GRASSSCENE + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + this.game.events.on('ui-update-hp', (hp) => { + this.gameState.hp = Phaser.Math.Clamp(hp, 0, this.gameState.maxHp); + this._redrawBars(); + this._checkCritical(); + }); + + this.game.events.on('ui-update-energy', (energy) => { + this.gameState.energy = Phaser.Math.Clamp(energy, 0, this.gameState.maxEnergy); + this._redrawBars(); + }); + + this.game.events.on('ui-update-time', (day, hour, minute) => { + this.gameState.day = day; + this.gameState.hour = hour; + this.gameState.minute = minute; + this._updateTimeDisplay(); + }); + + this.game.events.on('ui-show-dialog', (name, text) => { + this.showDialog(name, text); + }); + + this.game.events.on('ui-hide-dialog', () => { + this.hideDialog(); + }); + + // โ”€โ”€ Hotbar update iz InventorySystem โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + // Hotbar item text labels (nad sloti) + this._hotbarItemTexts = []; + this._hotbarCountTexts = []; + for (let i = 0; i < 7; i++) { + const { x, y } = this.hotbarSlots[i]; + const icon = this.add.text(x, y - 4, '', { + fontSize: '26px', + align: 'center', + }).setOrigin(0.5, 0.5).setDepth(1005); + + const cnt = this.add.text(x + 16, y + 14, '', { + fontFamily: 'Arial Black', + fontSize: '11px', + color: '#ffdd44', + stroke: '#000000', + strokeThickness: 3, + }).setOrigin(0.5, 0.5).setDepth(1006); + + this._hotbarItemTexts.push(icon); + this._hotbarCountTexts.push(cnt); + } + + // Gold counter (spodaj levo od hotbara) + this.goldText = this.add.text(W / 2 - HOTBAR_W / 2, H - PAD - 85, '๐Ÿ’ฐ 0g', { + fontFamily: 'Arial Black', + fontSize: '16px', + color: '#ffd700', + stroke: '#000000', + strokeThickness: 4, + }).setOrigin(0, 0.5).setDepth(1005); + + // Posluลกaj hotbar updete iz InventorySystem + this.events.on('hotbarUpdate', (data) => { + const { slots, activeSlot, gold } = data; + + // Posodobi slote + for (let i = 0; i < 7; i++) { + const slot = slots[i]; + const hl = this.hotbarSlots[i].highlight; + hl.clear(); + + // Active slot highlight + const { x, y } = this.hotbarSlots[i]; + const SW = this._slotW; + const SH = this._hotbarH; + if (i === activeSlot) { + hl.lineStyle(3, 0xffdd00, 1.0); + hl.strokeRoundedRect( + x - SW / 2 + 4, y - SH / 2 + 4, + SW - 8, SH - 8, 6 + ); + } + + // Icon in count + if (slot) { + this._hotbarItemTexts[i].setText(slot.icon || ''); + this._hotbarCountTexts[i].setText( + slot.stackable && slot.count > 0 ? `ร—${slot.count}` : '' + ); + } else { + this._hotbarItemTexts[i].setText(''); + this._hotbarCountTexts[i].setText(''); + } + } + + // Gold + if (this.goldText) this.goldText.setText(`๐Ÿ’ฐ ${gold}g`); + }); + + // โ”€โ”€ Time update iz DayNightSystem โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + this.events.on('timeChanged', (data) => { + if (this.timeText) { + this.timeText.setText(data.timeStr || '06:00'); + } + if (this.dayText) { + this.dayText.setText(`DAN ${data.day}`); + } + }); + + // โ”€โ”€ Water update iz WaterSystem โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + // Vedro indikator (blizu hotbara) + this.waterHUD = this.add.text(W / 2 + HOTBAR_W / 2 - 60, H - PAD - 85, '๐Ÿ’ง 3/5L', { + fontFamily: 'Arial Black', + fontSize: '14px', + color: '#44aaff', + stroke: '#000000', + strokeThickness: 3, + }).setOrigin(1, 0.5).setDepth(1005); + + this.events.on('waterChanged', (data) => { + if (this.waterHUD) { + const pct = data.can / data.canMax; + const col = pct > 0.5 ? '#44aaff' : pct > 0.2 ? '#ffaa00' : '#ff4444'; + this.waterHUD + .setText(`๐Ÿ’ง ${data.can}/${data.canMax}L`) + .setColor(col); + } + }); + + // UIScene teฤe vzporedno z GrassScene, prinese se na vrh + this.scene.bringToTop(); + + console.log('โœ… UIScene initialized with all elements!'); + } + + + // ========================================================= + // HELPERS + // ========================================================= + + /** Nariลกi HP in Energy bar fills */ + _redrawBars() { + const { hp, maxHp, energy, maxEnergy } = this.gameState; + + // HP bar + const hpPct = hp / maxHp; + const { x, y, w, h } = this._hpBar; + this.hpFill.clear(); + + // Barva glede na HP % + const hpColor = hpPct > 0.6 ? 0xcc2222 : + hpPct > 0.3 ? 0xdd7700 : 0xff0000; + + this.hpFill.fillStyle(hpColor, 1.0); + this.hpFill.fillRoundedRect(x, y, Math.max(4, w * hpPct), h, 8); + + // HP outer border + this.hpFill.lineStyle(2, 0x660000, 1.0); + this.hpFill.strokeRoundedRect(x, y, w, h, 8); + + this.hpText.setText(`${Math.round(hp)} / ${maxHp}`); + + // Energy bar + const enPct = energy / maxEnergy; + const e = this._enBar; + this.energyFill.clear(); + + const enColor = enPct > 0.5 ? 0xddcc00 : + enPct > 0.2 ? 0xdd8800 : 0xff4400; + + this.energyFill.fillStyle(enColor, 1.0); + this.energyFill.fillRoundedRect(e.x, e.y, Math.max(4, e.w * enPct), e.h, 6); + this.energyFill.lineStyle(2, 0x444400, 1.0); + this.energyFill.strokeRoundedRect(e.x, e.y, e.w, e.h, 6); + + this.energyText.setText(`${Math.round(energy)}%`); + } + + /** Preveri kritiฤen HP in prikaลพi overlay */ + _checkCritical() { + const pct = this.gameState.hp / this.gameState.maxHp; + if (pct < 0.25) { + // Pulse animacija + if (!this._criticalTween || !this._criticalTween.isPlaying()) { + this._criticalTween = this.tweens.add({ + targets: this.criticalOverlay, + alpha: { from: 0, to: 0.55 }, + duration: 600, + yoyo: true, + repeat: -1, + ease: 'Sine.easeInOut', + }); + } + } else { + // Ugasni + if (this._criticalTween) { + this._criticalTween.stop(); + this._criticalTween = null; + } + this.criticalOverlay.setAlpha(0); + } + } + + /** Posodobi uro + dan display */ + _updateTimeDisplay() { + const { day, hour, minute } = this.gameState; + const hStr = String(hour).padStart(2, '0'); + const mStr = String(minute).padStart(2, '0'); + this.timeText.setText(`${hStr}:${mStr}`); + this.dayText.setText(`DAN ${day}`); + + // Ura barva: rdeฤa ponoฤi, rumena podnevi + const isNight = hour >= 21 || hour < 5; + this.timeText.setColor(isNight ? '#8899ff' : '#ff3333'); + } + + /** Toggle Inventory popup */ + toggleInventory() { + this.gameState.inventoryOpen = !this.gameState.inventoryOpen; + const open = this.gameState.inventoryOpen; + + this.inventoryPanel.setVisible(open); + this.inventoryTitle.setVisible(open); + this.inventoryClose.setVisible(open); + this.invDim.setVisible(open); + + if (open) { + // Slide in animacija + this.inventoryPanel.setAlpha(0).setScale(0.65); + this.tweens.add({ + targets: this.inventoryPanel, + alpha: 1, + scale: 0.75, + duration: 200, + ease: 'Back.out', + }); + } + } + + /** Prikaลพi dialog box (typewriter) */ + showDialog(name, text) { + this.dialogPanel.setVisible(true); + this.dialogName.setVisible(true).setText(name || ''); + this.dialogText.setVisible(true).setText(''); + + // Typewriter efekt + let idx = 0; + if (this._typewriterTimer) this._typewriterTimer.remove(); + this._typewriterTimer = this.time.addEvent({ + delay: 35, + repeat: text.length - 1, + callback: () => { + this.dialogText.setText(text.substring(0, ++idx)); + } + }); + } + + /** Skrij dialog box */ + hideDialog() { + if (this._typewriterTimer) this._typewriterTimer.remove(); + this.dialogPanel.setVisible(false); + this.dialogText.setVisible(false); + this.dialogName.setVisible(false); + } + + // ========================================================= + // UPDATE โ€” Minimap Kai dot + // ========================================================= + update() { + // Posodaibaj Kai dot na minimapu + const grassScene = this.scene.get('GrassSceneClean'); + if (!grassScene || !grassScene.kai) return; + + const kai = grassScene.kai; + + // Svet bounds (iz GrassScene) + const worldW = grassScene.worldWidth || 10400; + const worldH = grassScene.worldHeight || 10400; + + // Normaliziraj Kai pozicijo [0..1] + const nx = kai.x / worldW; + const ny = kai.y / worldH; + + // Mapiraj na minimap krog + const angle = Math.atan2(ny - 0.5, nx - 0.5); + const dist = Math.min(Math.sqrt((nx - 0.5) ** 2 + (ny - 0.5) ** 2) * 2, 1); + + const dotX = this._mmCx + Math.cos(angle) * dist * this._mmR; + const dotY = this._mmCy + Math.sin(angle) * dist * this._mmR; + + this.minimapKaiDot.clear(); + this.minimapKaiDot.fillStyle(0x44ffaa, 1.0); + this.minimapKaiDot.fillCircle(dotX, dotY, 5); + + // Pulsirajoฤ rob na dotiku + this.minimapKaiDot.lineStyle(2, 0xffffff, 0.6); + this.minimapKaiDot.strokeCircle(dotX, dotY, 7); } } diff --git a/nova farma TRAE/src/systems/BuildingSystem.js b/nova farma TRAE/src/systems/BuildingSystem.js index b08ac5725..a60b99b58 100644 --- a/nova farma TRAE/src/systems/BuildingSystem.js +++ b/nova farma TRAE/src/systems/BuildingSystem.js @@ -2,43 +2,38 @@ * ============================================================ * BUILDING SYSTEM โ€” Nova Farma * ============================================================ - * Handles: - * - Build Mode (toggle with [B] key or UI button) - * - Ghost preview image that follows the cursor - * - Click to place building at cursor position - * - Y-sort depth so Kai walks behind / in front of buildings - * - Physics static body so Kai cannot walk through walls - * - Side-panel Build UI with selectable building icons + * [B] = vklopi / izklopi build mode + * [Esc] = izhod + * Klik = postavi stavbo + * + * UI meni je ODSTRANJEN โ€” stavbe se dodajajo programsko + * prek: buildingSystem.selectBuilding('sotor') * ============================================================ */ export default class BuildingSystem { /** - * @param {Phaser.Scene} scene - The scene this system runs in - * @param {Phaser.Physics.Arcade.Sprite} kai - Player sprite reference + * @param {Phaser.Scene} scene + * @param {Phaser.Physics.Arcade.Sprite} kai */ constructor(scene, kai) { this.scene = scene; this.kai = kai; - // Build mode state - this.active = false; // Is build mode ON? - this.selectedKey = null; // Currently selected building texture key - - // Ghost preview sprite + this.active = false; + this.selectedKey = null; this.ghost = null; + this.placed = []; - // Group of all placed buildings (static physics bodies) + // Static physics group za vse postavljene stavbe this.buildingsGroup = scene.physics.add.staticGroup(); - // Catalogue of buildable items - // { key, label, colliderW, colliderH, colliderOffsetX, colliderOffsetY, scale } - // Scale is % of Kai height (64px). + // Katalog stavb โ€” tukaj dodajamo nove this.catalogue = [ { key: 'sotor', label: 'ล otor', - scale: 2.5, // 2.5ร— Kai โ†’ 160px tall + scale: 2.5, // 2.5ร— Kai (64px) = 160px colliderW: 160, colliderH: 40, colliderOffsetX: -80, @@ -46,8 +41,8 @@ export default class BuildingSystem { }, { key: 'campfire', - label: 'Ogenj', - scale: 1.0, // Same height as Kai + label: 'Taborni ogenj', + scale: 1.0, colliderW: 50, colliderH: 20, colliderOffsetX: -25, @@ -64,7 +59,7 @@ export default class BuildingSystem { }, { key: 'foundation_concrete', - label: 'Temelj', + label: 'Betonski temelj', scale: 2.0, colliderW: 120, colliderH: 20, @@ -73,160 +68,37 @@ export default class BuildingSystem { }, ]; - // Build UI panel (DOM container) - this.panel = null; - - // Track placed buildings for saving/loading later - this.placed = []; - - this._setupUI(); this._setupInput(); } // ========================================== - // UI PANEL (Fixed to screen, top-right) - // ========================================== - _setupUI() { - const scene = this.scene; - const CAM_W = scene.cameras.main.width; - const PANEL_W = 80; - const PANEL_PADDING = 8; - const BTN_SIZE = 60; - const BTN_GAP = 12; - - // โ”€โ”€ Background Panel โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - this.panel = scene.add.graphics() - .setScrollFactor(0) - .setDepth(20000); - - const panelH = this.catalogue.length * (BTN_SIZE + BTN_GAP) + PANEL_PADDING * 2 + 40; - const panelX = CAM_W - PANEL_W - 16; - const panelY = 120; - - // Dark translucent panel - this.panel.fillStyle(0x0a0a0f, 0.85); - this.panel.fillRoundedRect(panelX, panelY, PANEL_W, panelH, 10); - this.panel.lineStyle(2, 0x44ffaa, 0.6); - this.panel.strokeRoundedRect(panelX, panelY, PANEL_W, panelH, 10); - - // "Build" label - scene.add.text( - panelX + PANEL_W / 2, - panelY + 10, - '[B]', - { fontSize: '13px', color: '#44ffaa', fontFamily: 'Arial', align: 'center' } - ).setOrigin(0.5, 0).setScrollFactor(0).setDepth(20001); - - // โ”€โ”€ Building Buttons โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - this.catalogue.forEach((item, i) => { - const btnX = panelX + PANEL_PADDING; - const btnY = panelY + 40 + i * (BTN_SIZE + BTN_GAP); - const btnW = PANEL_W - PANEL_PADDING * 2; - const btnH = BTN_SIZE; - - // Button background - const btnBg = scene.add.graphics() - .setScrollFactor(0) - .setDepth(20001); - btnBg.fillStyle(0x1a2a1a, 0.9); - btnBg.fillRoundedRect(btnX, btnY, btnW, btnH, 6); - btnBg.lineStyle(1, 0x224422, 1.0); - btnBg.strokeRoundedRect(btnX, btnY, btnW, btnH, 6); - - // Icon (preview of the building texture) - const icon = scene.add.image( - btnX + btnW / 2, - btnY + btnH / 2 - 8, - item.key - ) - .setScrollFactor(0) - .setDepth(20002) - .setDisplaySize(btnW - 8, btnH - 20); - - // Label text - const label = scene.add.text( - btnX + btnW / 2, - btnY + btnH - 10, - item.label, - { fontSize: '9px', color: '#aaffcc', fontFamily: 'Arial', align: 'center' } - ).setOrigin(0.5, 0.5).setScrollFactor(0).setDepth(20002); - - // Invisible hit zone for click detection - const hitZone = scene.add.zone(btnX, btnY, btnW, btnH) - .setOrigin(0, 0) - .setScrollFactor(0) - .setDepth(20003) - .setInteractive({ cursor: 'pointer' }); - - hitZone.on('pointerover', () => { - btnBg.clear(); - btnBg.fillStyle(0x2a4a2a, 1.0); - btnBg.fillRoundedRect(btnX, btnY, btnW, btnH, 6); - btnBg.lineStyle(2, 0x44ffaa, 1.0); - btnBg.strokeRoundedRect(btnX, btnY, btnW, btnH, 6); - }); - - hitZone.on('pointerout', () => { - const isSelected = this.selectedKey === item.key; - btnBg.clear(); - btnBg.fillStyle(isSelected ? 0x1a4a1a : 0x1a2a1a, 0.9); - btnBg.fillRoundedRect(btnX, btnY, btnW, btnH, 6); - btnBg.lineStyle(isSelected ? 2 : 1, isSelected ? 0x44ffaa : 0x224422, 1.0); - btnBg.strokeRoundedRect(btnX, btnY, btnW, btnH, 6); - }); - - hitZone.on('pointerdown', () => { - this.selectBuilding(item.key); - }); - - // Store references to update highlight state - item._btnBg = btnBg; - item._btnX = btnX; - item._btnY = btnY; - item._btnW = btnW; - item._btnH = btnH; - }); - } - - // ========================================== - // KEYBOARD: [B] toggles build mode + // KEYBOARD: [B] toggle, [Esc] izhod // ========================================== _setupInput() { - const scene = this.scene; - - // Toggle build mode with [B] - scene.input.keyboard.on('keydown-B', () => { - if (this.active) { - this.deactivate(); - } else { - this.activate(); - } + this.scene.input.keyboard.on('keydown-B', () => { + this.active ? this.deactivate() : this.activate(); }); - // Cancel / exit build mode with [Escape] - scene.input.keyboard.on('keydown-ESC', () => { + this.scene.input.keyboard.on('keydown-ESC', () => { if (this.active) this.deactivate(); }); } // ========================================== - // ACTIVATE BUILD MODE + // ACTIVATE // ========================================== activate() { this.active = true; - // If nothing selected yet, default to first item if (!this.selectedKey) { this.selectBuilding(this.catalogue[0].key); } else { this._createGhost(this.selectedKey); } - // Show tip text if (!this._modeTip) { 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', { fontSize: '16px', @@ -235,32 +107,22 @@ export default class BuildingSystem { strokeThickness: 3, fontFamily: 'Arial', backgroundColor: '#0a0a0f99', - padding: { x: 10, y: 4 } + padding: { x: 10, y: 4 }, } - ) - .setOrigin(0.5, 0) - .setScrollFactor(0) - .setDepth(20010); + ).setOrigin(0.5, 0).setScrollFactor(0).setDepth(20010); } this._modeTip.setVisible(true); } // ========================================== - // DEACTIVATE BUILD MODE + // DEACTIVATE // ========================================== deactivate() { this.active = false; this.selectedKey = null; - if (this.ghost) { - this.ghost.destroy(); - this.ghost = null; - } - + if (this.ghost) { this.ghost.destroy(); this.ghost = null; } if (this._modeTip) this._modeTip.setVisible(false); - - // Reset all button highlights - this.catalogue.forEach(item => this._drawBtn(item, false)); } // ========================================== @@ -270,77 +132,49 @@ export default class BuildingSystem { this.selectedKey = key; if (!this.active) this.activate(); - // Destroy old ghost - if (this.ghost) { - this.ghost.destroy(); - this.ghost = null; - } + if (this.ghost) { this.ghost.destroy(); this.ghost = null; } this._createGhost(key); - - // Update button highlights - this.catalogue.forEach(item => { - this._drawBtn(item, item.key === key); - }); - } - - _drawBtn(item, selected) { - if (!item._btnBg) return; - item._btnBg.clear(); - item._btnBg.fillStyle(selected ? 0x1a4a1a : 0x1a2a1a, 0.9); - item._btnBg.fillRoundedRect(item._btnX, item._btnY, item._btnW, item._btnH, 6); - item._btnBg.lineStyle(selected ? 2 : 1, selected ? 0x44ffaa : 0x224422, 1.0); - item._btnBg.strokeRoundedRect(item._btnX, item._btnY, item._btnW, item._btnH, 6); } // ========================================== - // GHOST PREVIEW SPRITE + // GHOST PREVIEW // ========================================== _createGhost(key) { const def = this.catalogue.find(c => c.key === key); if (!def) return; - const KAI_H = 64; // px + const KAI_H = 64; const targetH = KAI_H * def.scale; - const texture = this.scene.textures.get(key); - const srcH = texture.getSourceImage().height; + const srcH = this.scene.textures.get(key).getSourceImage().height; const scale = targetH / srcH; this.ghost = this.scene.add.image(0, 0, key) .setScale(scale) - .setOrigin(0.5, 1.0) // Feet at cursor + .setOrigin(0.5, 1.0) .setAlpha(0.6) - .setTint(0x44ffaa) // Green tint = valid placement + .setTint(0x44ffaa) .setDepth(99999); - - // Make ghost follow pointer in update() } // ========================================== - // UPDATE (call from scene's update()) + // UPDATE โ€” klic iz scene.update() // ========================================== update(pointer) { if (!this.active || !this.ghost) return; - // Ghost follows world-space cursor - const worldX = pointer.worldX; - const worldY = pointer.worldY; - this.ghost.setPosition(worldX, worldY); + const wx = pointer.worldX; + const wy = pointer.worldY; + this.ghost.setPosition(wx, wy); - // Check if placement is on the island (within physics bounds) - const bounds = this.scene.physics.world.bounds; - const onIsland = ( - worldX > bounds.x && - worldX < bounds.x + bounds.width && - worldY > bounds.y && - worldY < bounds.y + bounds.height - ); - - // Green = valid, Red = invalid - this.ghost.setTint(onIsland ? 0x44ffaa : 0xff4444); + // Zelena = na otoku, Rdeฤa = zunaj + const b = this.scene.physics.world.bounds; + const ok = wx > b.x && wx < b.x + b.width && + wy > b.y && wy < b.y + b.height; + this.ghost.setTint(ok ? 0x44ffaa : 0xff4444); } // ========================================== - // PLACE BUILDING (called on click) + // PLACE BUILDING (ob kliku) // ========================================== placeBuilding(worldX, worldY) { if (!this.active || !this.selectedKey) return; @@ -348,87 +182,73 @@ export default class BuildingSystem { const def = this.catalogue.find(c => c.key === this.selectedKey); if (!def) return; - // Validate placement is on island - const bounds = this.scene.physics.world.bounds; - if ( - worldX <= bounds.x || worldX >= bounds.x + bounds.width || - worldY <= bounds.y || worldY >= bounds.y + bounds.height - ) { - // Flash ghost red - this.scene.tweens.add({ - targets: this.ghost, - alpha: { from: 0.9, to: 0.3 }, - duration: 100, - yoyo: true, - repeat: 2, - }); + // Validacija โ€” mora biti na otoku + const b = this.scene.physics.world.bounds; + if (worldX <= b.x || worldX >= b.x + b.width || + worldY <= b.y || worldY >= b.y + b.height) { + // 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; } - // โ”€โ”€ Calculate final scale โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + // Izraฤun scale const KAI_H = 64; const targetH = KAI_H * def.scale; const texture = this.scene.textures.get(this.selectedKey); const srcH = texture.getSourceImage().height; const scale = targetH / srcH; - // โ”€โ”€ Create placed sprite โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + // Ustvari static physics image const building = this.scene.physics.add.staticImage(worldX, worldY, this.selectedKey) .setScale(scale) - .setOrigin(0.5, 1.0); + .setOrigin(0.5, 1.0) + .setDepth(worldY); - // Y-sort: depth is based on the foot Y position (worldY) so Kai sorts properly - building.setDepth(worldY); - - // โ”€โ”€ Physics collider body โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - // Adjust physics body to cover just the base of the building - const bodyW = def.colliderW; - const bodyH = def.colliderH; - const offsetX = srcH * scale * 0 + def.colliderOffsetX; // center-ish - const offsetY = def.colliderOffsetY; // above feet - building.body.setSize(bodyW, bodyH); + // Physics collider body (samo baza stavbe) + building.body.setSize(def.colliderW, def.colliderH); building.body.setOffset( (texture.getSourceImage().width * scale) / 2 + def.colliderOffsetX, srcH * scale + def.colliderOffsetY ); building.body.reset(worldX, worldY); - // โ”€โ”€ Add to static group & collider โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + // Collider med Kai in stavbo this.buildingsGroup.add(building, true); this.scene.physics.add.collider(this.kai, building); - // โ”€โ”€ Spawn animation โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + // Spawn animacija building.setAlpha(0); this.scene.tweens.add({ targets: building, alpha: 1, - scaleX: scale, - scaleY: scale, - duration: 300, + duration: 280, ease: 'Back.out', }); - // Save for later - this.placed.push({ key: this.selectedKey, x: worldX, y: worldY }); - - // โ”€โ”€ Update depth every frame (for Y-sort) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - // Store ref so update() can sort depths - building._isBuilding = true; - this.scene._buildingSprites = this.scene._buildingSprites || []; - this.scene._buildingSprites.push(building); - - // Small flash feedback - const flash = this.scene.add.rectangle(worldX, worldY - targetH * 0.5, 80, 80, 0x44ffaa, 0.7) + // Flash feedback + const flash = this.scene.add.rectangle(worldX, worldY - targetH * 0.5, 80, 80, 0x44ffaa, 0.6) .setDepth(99998); this.scene.tweens.add({ targets: flash, - alpha: 0, - scaleX: 2, - scaleY: 2, - duration: 400, + alpha: 0, scaleX: 2, scaleY: 2, + duration: 350, onComplete: () => flash.destroy(), }); - console.log(`๐Ÿ—๏ธ Placed: ${this.selectedKey} at (${Math.round(worldX)}, ${Math.round(worldY)})`); + // Shrani referenco za Y-sort v update() + 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(worldX)}, ${Math.round(worldY)})`); } } diff --git a/nova farma TRAE/src/systems/DayNightSystem.js b/nova farma TRAE/src/systems/DayNightSystem.js new file mode 100644 index 000000000..0cbc2dea1 --- /dev/null +++ b/nova farma TRAE/src/systems/DayNightSystem.js @@ -0,0 +1,441 @@ +/** + * ============================================================ + * DayNightSystem.js โ€” Nova Farma + * ============================================================ + * Realen dan/noฤ cikel z vsemi hook-i: + * + * - Vsaka sekunda realna = 1 minuta v igri (24 min real = 1 dan) + * - 06:00 = Jutro (svetlo, ptice) + * - 12:00 = Poldne (svetlo max) + * - 20:00 = Veฤer (tema pada, oranลพna) + * - 22:00 = Noฤ (modra tema, zvezde) + * - 00:00 = Polnoฤ โ†’ nov dan trigger + * + * Events emittirani: + * - 'dayChanged' { day } โ†’ FarmingSystem.onNewDay(day) + * - 'morningArrived' { day } โ†’ Budnica / ptice / energija reset + * - 'nightArrived' { day } โ†’ Zombiji aktiv + * - 'hourChanged' { hour, minute } โ†’ UIScene clock update + * + * Spanje: + * - [Z] blizu spalne vreฤe/ลกotora โ†’ Sleep โ†’ skok na 06:00 naslednjega dne + * ============================================================ + */ + +export default class DayNightSystem { + + /** + * @param {Phaser.Scene} scene + * @param {object} options + */ + constructor(scene, options = {}) { + this.scene = scene; + + // ฤŒas + this.day = 1; + this.hour = 6; // Zaฤne ob 6:00 + this.minute = 0; + + // Hitrost: 1 real sekunda = N minut v igri + // Default: 24 real minut = 1 dan โ†’ 1 real sec = 1 igra minuta + this.gameMinutesPerRealSecond = options.speed || 1; + + // Interaktivni objekti za spanje + this.sleepObjects = []; // { x, y, sprite, type } + this.isSleeping = false; + this.interactRange = 90; + + // Input [Z] za spanje + this.keyZ = scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.Z); + + // Ambient Graphics (overlay za noฤ) + this.ambientOverlay = scene.add.graphics() + .setScrollFactor(0) + .setDepth(4500); // Pod UIScene (depth 5000+), nad svetom + + // Star particles za noฤ + this.stars = []; + this._initStars(); + + // ฤŒas tracking + this._accumMs = 0; + + // Callback cache + this._lastHour = -1; + this._lastDay = -1; + + // Sleep prompt text + this._sleepPrompt = null; + this._highlightGfx = scene.add.graphics().setDepth(9002); + + console.log(`๐ŸŒ… DayNightSystem initialized โ€” Dan ${this.day}, ${this._timeStr()}`); + } + + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + // UPDATE โ€” Kliฤi iz scene.update() z delta + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + update(delta, kai) { + if (this.isSleeping) return; + + // โ”€โ”€ Tick ฤas โ”€โ”€ + this._accumMs += delta; + const msPerGameMin = 1000 / this.gameMinutesPerRealSecond; + + while (this._accumMs >= msPerGameMin) { + this._accumMs -= msPerGameMin; + this._tickMinute(); + } + + // โ”€โ”€ Ambient svetloba โ”€โ”€ + this._updateAmbient(); + + // โ”€โ”€ Zvezde โ”€โ”€ + this._updateStars(); + + // โ”€โ”€ Sleep interakcija โ”€โ”€ + this._highlightGfx.clear(); + const nearSleep = this._getSleepNear(kai); + if (nearSleep) { + this._highlightGfx.lineStyle(2, 0xaaaaff, 0.8); + this._highlightGfx.strokeCircle(nearSleep.x, nearSleep.y, 50); + this._showSleepPrompt(nearSleep); + } else { + this._hideSleepPrompt(); + } + + if (Phaser.Input.Keyboard.JustDown(this.keyZ)) { + if (nearSleep) { + this.sleep(); + } + } + } + + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + // TICK minuta + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + _tickMinute() { + this.minute++; + if (this.minute >= 60) { + this.minute = 0; + this.hour++; + this._onHourChange(); + } + if (this.hour >= 24) { + this.hour = 0; + this._onMidnight(); + } + + // Emit UI update vsako minuto + this.scene.events.emit('timeChanged', { + day: this.day, + hour: this.hour, + minute: this.minute, + timeStr: this._timeStr(), + }); + } + + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + // HOOK โ€” Vsaka ura + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + _onHourChange() { + this.scene.events.emit('hourChanged', { + day: this.day, + hour: this.hour, + minute: this.minute, + }); + + // Posebni uri: + if (this.hour === 6) { + // Jutro! + this.scene.events.emit('morningArrived', { day: this.day }); + console.log(`๐ŸŒ… Jutro! Dan ${this.day}, 06:00`); + } + if (this.hour === 20) { + // Veฤer + this.scene.events.emit('eveningArrived', { day: this.day }); + console.log(`๐ŸŒ† Veฤer โ€” zombiji se bujajo...`); + } + if (this.hour === 22) { + // Noฤ + this.scene.events.emit('nightArrived', { day: this.day }); + console.log(`๐ŸŒ™ Noฤ! Be careful, Kai...`); + } + } + + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + // HOOK โ€” Polnoฤ โ†’ Nov dan + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + _onMidnight() { + this.day++; + console.log(`๐ŸŒ™ Polnoฤ โ†’ Dan ${this.day}!`); + + // โ”€โ”€ Farming tick โ”€โ”€ + this.scene.events.emit('dayChanged', { day: this.day }); + + // โ”€โ”€ Rain check (30% chance) โ”€โ”€ + if (Math.random() < 0.3) { + this.scene.events.emit('rainDay', { day: this.day }); + } + } + + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + // SPANJE โ€” Skip na 06:00 naslednjega dne + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + sleep() { + if (this.isSleeping) return; + // Dovoli spanje samo med 20:00 in 24:00 ali 00:00-06:00 + if (this.hour >= 6 && this.hour < 20) { + this._showFloatingText( + this.scene.cameras.main.width / 2, + this.scene.cameras.main.height / 2 - 60, + '๐Ÿ˜ด Preuranjeno za spati! (po 20:00)', + '#ffaa44', 20, true + ); + return; + } + + this.isSleeping = true; + console.log(`๐Ÿ˜ด Kai gre spat... (${this._timeStr()} โ†’ 06:00)`); + + // Fade black screen + this.scene.cameras.main.fadeOut(800, 0, 0, 0); + + this.scene.time.delayedCall(900, () => { + // Preskoฤi na jutro + const prevDay = this.day; + if (this.hour >= 20 || this.hour < 6) { + this._onMidnight(); // Nov dan + } + this.hour = 6; + this.minute = 0; + + // Emit morning events + this.scene.events.emit('morningArrived', { day: this.day }); + this.scene.events.emit('timeChanged', { + day: this.day, hour: 6, minute: 0, timeStr: '06:00' + }); + + // Fade back in + this.scene.cameras.main.fadeIn(1200, 0, 0, 0); + + // Prikaลพi "Dan X" overlay + this._showDayOverlay(this.day); + + this.scene.time.delayedCall(1300, () => { + this.isSleeping = false; + }); + }); + } + + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + // AMBIENT OSVETLITEV + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + _updateAmbient() { + const W = this.scene.cameras.main.width; + const H = this.scene.cameras.main.height; + const h = this.hour + this.minute / 60; + + let alpha = 0; + let r = 0, g = 0, b = 0; + + if (h >= 6 && h < 8) { + // Zora: oranลพno-roza tema + const t = (h - 6) / 2; + alpha = 0.25 * (1 - t); + r = 0xff; g = 0x88; b = 0x00; + } else if (h >= 8 && h < 18) { + // Dan: brez overlaya + alpha = 0; + } else if (h >= 18 && h < 20) { + // Sonฤni zahod: oranลพna + const t = (h - 18) / 2; + alpha = 0.15 + t * 0.2; + r = 0xff; g = 0x66; b = 0x00; + } else if (h >= 20 && h < 22) { + // Veฤer: temno modra + const t = (h - 20) / 2; + alpha = 0.35 + t * 0.25; + r = 0x00; g = 0x00; b = 0x44; + } else if (h >= 22 || h < 4) { + // Noฤ: globoka modra tema + alpha = 0.60; + r = 0x00; g = 0x00; b = 0x22; + } else if (h >= 4 && h < 6) { + // Pred zoro + const t = (h - 4) / 2; + alpha = 0.60 * (1 - t * 0.5); + r = 0x00; g = 0x00; b = 0x22; + } + + this.ambientOverlay.clear(); + if (alpha > 0.01) { + this.ambientOverlay.fillStyle( + (r << 16) | (g << 8) | b, + alpha + ); + this.ambientOverlay.fillRect(0, 0, W, H); + } + } + + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + // ZVEZDE (Noฤ) + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + _initStars() { + const W = this.scene.cameras.main.width; + const H = this.scene.cameras.main.height; + this.starGfx = this.scene.add.graphics() + .setScrollFactor(0) + .setDepth(4499); + + for (let i = 0; i < 80; i++) { + this.stars.push({ + x: Math.random() * W, + y: Math.random() * H * 0.6, // Samo zgoraj + size: 0.5 + Math.random() * 1.5, + twinkle: Math.random() * Math.PI * 2, + speed: 0.5 + Math.random() * 1.5, + }); + } + } + + _updateStars() { + const h = this.hour + this.minute / 60; + // Stars visible 21:00 - 05:00 + let starAlpha = 0; + if (h >= 21 || h < 5) { + starAlpha = (h >= 21) ? Math.min(1, (h - 21) / 0.5) : Math.min(1, (5 - h) / 0.5); + } + + this.starGfx.clear(); + if (starAlpha < 0.01) return; + + const t = Date.now() * 0.001; + for (const star of this.stars) { + const twinkle = 0.4 + 0.6 * Math.sin(t * star.speed + star.twinkle); + this.starGfx.fillStyle(0xffffff, starAlpha * twinkle); + this.starGfx.fillCircle(star.x, star.y, star.size); + } + } + + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + // SLEEP OBJECTS MANAGEMENT + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + registerSleepObject(x, y, sprite, type = 'sleeping_bag') { + this.sleepObjects.push({ x, y, sprite, type }); + console.log(`๐Ÿ˜ด Sleep object registered: ${type} @ ${x},${y}`); + } + + _getSleepNear(kai) { + let closest = null; + let minDist = this.interactRange; + for (const obj of this.sleepObjects) { + const d = Phaser.Math.Distance.Between(kai.x, kai.y, obj.x, obj.y); + if (d < minDist) { minDist = d; closest = obj; } + } + return closest; + } + + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + // UI HELPERS + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + _timeStr() { + return `${String(this.hour).padStart(2, '0')}:${String(this.minute).padStart(2, '0')}`; + } + + _showSleepPrompt(obj) { + if (!this._sleepPrompt) { + this._sleepPrompt = this.scene.add.text(0, 0, '', { + fontFamily: 'Arial Black', + fontSize: '14px', + color: '#aaaaff', + stroke: '#000000', + strokeThickness: 4, + backgroundColor: '#00000099', + padding: { x: 8, y: 4 }, + }).setDepth(9999).setScrollFactor(1); + } + const label = (this.hour >= 20 || this.hour < 6) + ? `๐Ÿ˜ด [Z] Pojdi spat` + : `๐ŸŒ… [Z] Spanje (po 20:00)`; + this._sleepPrompt.setText(label).setAlpha(1).setPosition(obj.x - 60, obj.y - 80); + } + + _hideSleepPrompt() { + if (this._sleepPrompt) this._sleepPrompt.setAlpha(0); + } + + _showDayOverlay(day) { + const W = this.scene.cameras.main.width; + const H = this.scene.cameras.main.height; + + const txt = this.scene.add.text(W / 2, H / 2, `โ˜€๏ธ DAN ${day}`, { + fontFamily: 'Arial Black', + fontSize: '64px', + color: '#ffdd44', + stroke: '#000000', + strokeThickness: 8, + }).setOrigin(0.5).setScrollFactor(0).setDepth(6000).setAlpha(0); + + this.scene.tweens.add({ + targets: txt, + alpha: { from: 0, to: 1 }, + y: H / 2 - 20, + duration: 600, + ease: 'Back.out', + onComplete: () => { + this.scene.time.delayedCall(1200, () => { + this.scene.tweens.add({ + targets: txt, + alpha: 0, + y: H / 2 - 60, + duration: 500, + onComplete: () => txt.destroy(), + }); + }); + } + }); + } + + _showFloatingText(x, y, msg, color, size = 20, fixedToCamera = false) { + const txt = this.scene.add.text(x, y, msg, { + fontFamily: 'Arial Black', + fontSize: `${size}px`, + color, + stroke: '#000000', + strokeThickness: 4, + }).setOrigin(0.5).setDepth(9998); + if (fixedToCamera) txt.setScrollFactor(0); + + this.scene.tweens.add({ + targets: txt, + y: y - 50, + alpha: { from: 1, to: 0 }, + duration: 2000, + onComplete: () => txt.destroy(), + }); + } + + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + // PUBLIC API + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + getTimeStr() { return this._timeStr(); } + getDay() { return this.day; } + getHour() { return this.hour; } + isNight() { return this.hour >= 22 || this.hour < 6; } + isDay() { return this.hour >= 6 && this.hour < 20; } + + /** Nastavi ฤas (za debug/testing) */ + setTime(hour, minute = 0) { + this.hour = hour; + this.minute = minute; + console.log(`โฐ ฤŒas nastavljen: ${this._timeStr()}`); + } + + destroy() { + this.ambientOverlay?.destroy(); + this.starGfx?.destroy(); + this.starGfx = null; + this._sleepPrompt?.destroy(); + this._highlightGfx?.destroy(); + } +} diff --git a/nova farma TRAE/src/systems/FarmingSystem.js b/nova farma TRAE/src/systems/FarmingSystem.js new file mode 100644 index 000000000..ea81110ba --- /dev/null +++ b/nova farma TRAE/src/systems/FarmingSystem.js @@ -0,0 +1,660 @@ +/** + * ============================================================ + * FarmingSystem.js โ€” Nova Farma + * ============================================================ + * Farming logika: + * - [E] blizu tal โ†’ Ore (Hoe) โ†’ tilled_soil tile + * - [E] blizu tilled tile (z semenom v roki) โ†’ Posadi + * - [E] blizu rastline (z zalivalko v roki) โ†’ Zalij + * - Rast po N dnevih (tick ob sleep/new day) + * - [E] blizu zrele rastline โ†’ Harvest โ†’ item v inventar + * - Brez zalivanja 1 dan โ†’ rastlina uvene โ†’ izguba + * ============================================================ + * + * CROP DEFINITIONS: + * wheat: 4 stopnje, 3 dni, 30g semena, 3ร—30g harvest + * carrot: 5 stopenj, 4 dni, 15g semena, 2ร—40g harvest + * cannabis: 6 stopenj, 7 dni, 50g semena, 4ร—200g harvest (Faza 1) + */ + +export default class FarmingSystem { + + /** + * @param {Phaser.Scene} scene โ€” GrassSceneClean instanca + * @param {object} options โ€” { islandX, islandY, tileSize } + */ + constructor(scene, options = {}) { + this.scene = scene; + this.islandX = options.islandX || 2000; + this.islandY = options.islandY || 2000; + this.tileSize = options.tileSize || 128; + + // Crop definicije + this.CROPS = { + wheat: { + key: 'wheat', + stages: 4, // 0=sprout,1=small,2=medium,3=ripe + daysToGrow: 3, // Dan od saditve do ลพetve + seedCost: 10, + harvestItem: 'wheat', + harvestCount: [1, 3], // min,max + harvestValue: 30, + stageKeys: [ + 'crop_wheat_stage0', + 'crop_wheat_stage1', + 'crop_wheat_stage2', + 'crop_wheat_stage3', + ], + }, + carrot: { + key: 'carrot', + stages: 5, + daysToGrow: 4, + seedCost: 15, + harvestItem: 'carrot', + harvestCount: [1, 2], + harvestValue: 40, + stageKeys: [ + 'crop_carrot_stage0', + 'crop_carrot_stage1', + 'crop_carrot_stage2', + 'crop_carrot_stage3', + 'crop_carrot_stage4', + ], + }, + cannabis: { + key: 'cannabis', + stages: 6, + daysToGrow: 7, + seedCost: 50, + harvestItem: 'cannabis_bud', + harvestCount: [3, 5], + harvestValue: 200, + stageKeys: [ + 'crop_cannabis_stage0', + 'crop_cannabis_stage1', + 'crop_cannabis_stage2', + 'crop_cannabis_stage3', + 'crop_cannabis_stage4', + 'crop_cannabis_stage5', + ], + }, + }; + + // Stanje + this.tilledPlots = []; // { x, y, tileSprite, crop, dayPlanted, dayWatered, stage, sprite, wilted } + this.currentDay = 1; + this.interactRange = 60; // px โ€” razdalja za interakcijo + this.TILE_DISPLAY = 40; // Vizualna velikost tilled tile โ€” majhna kocka! + + // Aktiven tool v roki (set from InventorySystem) + this.activeTool = 'hoe'; // 'hoe' | 'watering_can' | null + this.activeSeed = 'wheat'; // 'wheat' | 'carrot' | 'cannabis' + + // Physics group za tilled tile colliderje + this.plotGroup = scene.physics.add.staticGroup(); + + // Graphics za highlight (Kai blizu) + this.highlightGfx = scene.add.graphics().setDepth(9000); + + // Input โ€” E tipka + this.keyE = scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.E); + + // Debounce + this._lastInteract = 0; + + console.log('๐ŸŒฑ FarmingSystem initialized'); + } + + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + // PRELOAD โ€” Assets (kliฤi iz scene.preload PRED new FarmingSystem) + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + static preload(scene) { + // Tilled soil + scene.load.image('tilled_soil', 'DEMO_FAZA1/Crops/tilled_soil.png'); + + // Wheat (4 stopnje) + for (let i = 0; i < 4; i++) { + scene.load.image(`crop_wheat_stage${i}`, `DEMO_FAZA1/Crops/crop_wheat_stage${i}.png`); + } + // Carrot (5 stopenj) + for (let i = 0; i < 5; i++) { + scene.load.image(`crop_carrot_stage${i}`, `DEMO_FAZA1/Crops/crop_carrot_stage${i}.png`); + } + // Cannabis (6 stopenj) + for (let i = 0; i < 6; i++) { + scene.load.image(`crop_cannabis_stage${i}`, `DEMO_FAZA1/Crops/crop_cannabis_stage${i}.png`); + } + } + + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + // UPDATE โ€” Kliฤi iz scene.update() + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + update(kai, time) { + this.highlightGfx.clear(); + + const nearPlot = this._getPlotNear(kai); + const nearEmpty = this._getEmptyGroundNear(kai); + + // โ”€โ”€ Highlight najbliลพji plot โ”€โ”€ + const W = 48, H = 24; // Velikost tilled plot kocke + if (nearPlot) { + this.highlightGfx.lineStyle(2, 0x44ffcc, 0.9); + this.highlightGfx.strokeRect( + nearPlot.x - W / 2, + nearPlot.y - H / 2, + W, H + ); + // Tooltip nad plotom + this._drawTooltip(nearPlot); + } else if (nearEmpty && this.activeTool === 'hoe') { + // Pregled oranja + this.highlightGfx.lineStyle(2, 0xaabb88, 0.5); + this.highlightGfx.strokeRect( + nearEmpty.x - W / 2, + nearEmpty.y - H / 2, + W, H + ); + } + + // โ”€โ”€ E tipka โ€” interakcija โ”€โ”€ + if (Phaser.Input.Keyboard.JustDown(this.keyE)) { + if (nearPlot) { + this._interactWithPlot(nearPlot, kai); + } else if (nearEmpty && this.activeTool === 'hoe') { + this._tillGround(nearEmpty.x, nearEmpty.y); + } + } + } + + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + // ORE (TILL) โ€” Ustvari nov tilled plot + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + _tillGround(x, y) { + // Snap na grid โ€” center tile-a + const ts = this.tileSize; + const snappedX = Math.floor(x / ts) * ts + ts / 2; + const snappedY = Math.floor(y / ts) * ts + ts / 2; + + // Ali plot ลพe obstaja? + if (this.tilledPlots.some(p => Math.abs(p.x - snappedX) < 4 && Math.abs(p.y - snappedY) < 4)) return; + + // === Nariลกi tilled soil z Graphics โ€” toฤna velikost, brez skaliranja slik === + const W = 48; // ลกirina kocke (world px) + const H = 24; // viลกina kocke (world px) + const gfx = this.scene.add.graphics(); + gfx.setDepth(snappedY - 50); + + // Ozadje โ€” temna rjava zemlja + gfx.fillStyle(0x3a2010, 1); + gfx.fillRect(snappedX - W / 2, snappedY - H / 2, W, H); + + // Brazde (bela linija ร— 3, horizontalne) + gfx.lineStyle(1, 0x6b4020, 0.7); + const lines = 3; + for (let i = 1; i <= lines; i++) { + const ly = snappedY - H / 2 + (H / (lines + 1)) * i; + gfx.beginPath(); + gfx.moveTo(snappedX - W / 2 + 3, ly); + gfx.lineTo(snappedX + W / 2 - 3, ly); + gfx.strokePath(); + } + + // Tanka okvirna linija + gfx.lineStyle(1, 0x8b5a2b, 0.5); + gfx.strokeRect(snappedX - W / 2, snappedY - H / 2, W, H); + + // Pop-in animacija โ€” skali iz 0 + gfx.setScale(0.1); + this.scene.tweens.add({ + targets: gfx, + scaleX: 1.0, + scaleY: 1.0, + duration: 250, + ease: 'Back.out', + }); + + // Shrani referenฤni objekt (tile = gfx) + const tile = gfx; + + // Physics static body za collider + const body = this.plotGroup.create(snappedX, snappedY); + body.setDisplaySize(this.tileSize - 4, this.tileSize - 4); + body.setAlpha(0); + body.refreshBody(); + + // Shrani plot + const plot = { + x: snappedX, + y: snappedY, + tileSprite: tile, + bodyRef: body, + crop: null, + dayPlanted: null, + dayWatered: null, + stage: 0, + sprite: null, + wilted: false, + }; + this.tilledPlots.push(plot); + + // Animacija: "Pop" ob oranju + this.scene.tweens.add({ + targets: tile, + scaleX: { from: 1.1, to: 1.0 }, + scaleY: { from: 0.8, to: 1.0 }, + duration: 200, + ease: 'Bounce.out', + }); + + this._showFloatingText(snappedX, snappedY, '๐Ÿชฑ Poorano!', '#aabb88'); + console.log(`๐ŸŒฑ Poorano: ${snappedX}, ${snappedY}`); + } + + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + // INTERAKCIJA Z PLOTOM (posadi / zalij / poลพeni) + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + _interactWithPlot(plot, kai) { + if (plot.wilted) { + // Uvela rastlina โ€” odstrani in reciklira plot + this._removeCrop(plot); + this._showFloatingText(plot.x, plot.y, '๐Ÿ‚ Odstranjeno', '#cc6666'); + return; + } + + if (!plot.crop) { + // 1. Prazno poorano โ†’ Posadi + if (this.activeTool === 'hoe' || this.activeSeed) { + this._plantCrop(plot, this.activeSeed); + } + } else if (plot.crop && plot.stage < this._getCropDef(plot.crop).stages - 1) { + // 2. Rastlina raste โ†’ Zalij (samo z zalivalko) + if (this.activeTool === 'watering_can') { + this._waterCrop(plot); + } else { + this._showFloatingText(plot.x, plot.y - 30, '๐Ÿ’ง Vzemi zalivalko!', '#aaaaff'); + } + } else if (plot.stage >= this._getCropDef(plot.crop).stages - 1 && !plot.wilted) { + // 3. Zrela โ†’ Poลพeni + this._harvestCrop(plot); + } + } + + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + // POSADI + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + _plantCrop(plot, cropKey) { + const def = this._getCropDef(cropKey); + if (!def) { + console.warn('Unknown crop:', cropKey); + return; + } + + plot.crop = cropKey; + plot.dayPlanted = this.currentDay; + plot.dayWatered = this.currentDay; // Zalijeลก takoj ob sajenju + plot.stage = 0; + plot.wilted = false; + + // Ustvari sprite za rastlino (stage 0) + // Stage 0 = 20px visoka (klica), max stage = 70px (zrela rastlina) + const stageKey = def.stageKeys[0]; + const cropH = 20; + const texture = this.scene.textures.get(stageKey); + const srcH = texture?.getSourceImage()?.height || 100; + const scale = cropH / srcH; + + // Posadi na center tilled tile-a z offset za vizualni izgled + plot.sprite = this.scene.add.image(plot.x, plot.y, stageKey) + .setScale(scale) + .setOrigin(0.5, 1.0) + .setDepth(plot.y + 1) + .setAlpha(0); + + // Pop-in animacija + this.scene.tweens.add({ + targets: plot.sprite, + alpha: 1, + scaleX: { from: scale * 1.4, to: scale }, + scaleY: { from: scale * 0.5, to: scale }, + duration: 350, + ease: 'Back.out', + }); + + this._showFloatingText(plot.x, plot.y - 30, `๐ŸŒฑ ${cropKey}!`, '#88ff88', 18); + console.log(`๐ŸŒฑ Posajeno: ${cropKey} @ day ${this.currentDay}`); + } + + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + // ZALIJ + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + _waterCrop(plot) { + if (!plot.crop) return; + if (plot.dayWatered === this.currentDay) { + this._showFloatingText(plot.x, plot.y - 30, 'โœ… ลฝe zalivano!', '#44ffcc'); + return; + } + + // === PREVERI VODO v vedru === + if (this.waterSystem) { + if (!this.waterSystem.hasWater(1)) { + this._showFloatingText(plot.x, plot.y - 30, + 'โŒ Vedro prazno! [F] pri RC', '#ff6666', 16); + return; + } + this.waterSystem.useWater(1); // Porabi 1L + } + + plot.dayWatered = this.currentDay; + plot.wilted = false; + + // Ripple efekt โ€” modri krogci + this._waterRipple(plot.x, plot.y); + this._showFloatingText(plot.x, plot.y - 20, '๐Ÿ’ง Zalivano!', '#44aaff'); + + // Tla (Graphics) - prebarva temnejลกe ko so mokra + if (plot.tileSprite) { + plot.tileSprite.clear(); + const W = 48, H = 24; + const snappedX = plot.x, snappedY = plot.y; + // Mokra zemlja = temnejลกa + plot.tileSprite.fillStyle(0x25140a, 1); + plot.tileSprite.fillRect(snappedX - W / 2, snappedY - H / 2, W, H); + plot.tileSprite.lineStyle(1, 0x4a2c10, 0.7); + for (let i = 1; i <= 3; i++) { + const ly = snappedY - H / 2 + (H / 4) * i; + plot.tileSprite.beginPath(); + plot.tileSprite.moveTo(snappedX - W / 2 + 3, ly); + plot.tileSprite.lineTo(snappedX + W / 2 - 3, ly); + plot.tileSprite.strokePath(); + } + } + } + + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + // ลฝETEV + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + _harvestCrop(plot) { + const def = this._getCropDef(plot.crop); + const count = Phaser.Math.Between(def.harvestCount[0], def.harvestCount[1]); + const value = count * def.harvestValue; + + // Notify scene (UIScene bo prikazal) + this.scene.events.emit('harvest', { + item: def.harvestItem, + count, + value, + x: plot.x, + y: plot.y, + }); + + // Floating nagrade tekst + this._showFloatingText(plot.x, plot.y - 20, + `๐ŸŒพ ร—${count} (+${value}g)`, '#ffd700', 28); + + // Bounce animacija preden izgine + if (plot.sprite) { + this.scene.tweens.add({ + targets: plot.sprite, + scaleX: plot.sprite.scaleX * 1.3, + scaleY: plot.sprite.scaleY * 1.3, + alpha: 0, + duration: 400, + ease: 'Back.in', + onComplete: () => { if (plot.sprite) plot.sprite.destroy(); } + }); + } + + // Reset plota (poorano ostane, crop zbrisano) + plot.crop = null; + plot.sprite = null; + plot.dayPlanted = null; + plot.dayWatered = null; + plot.stage = 0; + plot.wilted = false; + + // Tla postanejo svetlejลกa (prazna) + this.scene.tweens.add({ + targets: plot.tileSprite, + alpha: 0.9, + duration: 500, + }); + + console.log(`๐ŸŒพ Harvest: ${def.harvestItem} ร—${count} = ${value}g`); + } + + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + // DNEVNI TICK โ€” Kliฤi enkrat na dan (ob sleep/new day) + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + onNewDay(day) { + this.currentDay = day; + + for (const plot of this.tilledPlots) { + if (!plot.crop || plot.wilted) continue; + + const def = this._getCropDef(plot.crop); + + // โ”€โ”€ Preveri zalivanje โ”€โ”€ + if (plot.dayWatered < day - 1) { + // Ni bilo zalivano vฤeraj โ†’ Uvene! + this._wiltCrop(plot); + continue; + } + + // โ”€โ”€ Rast โ”€โ”€ + const daysGrown = day - plot.dayPlanted; + // Enakomerno razporedimo stopnje ฤez daysToGrow + const newStage = Math.min( + Math.floor((daysGrown / def.daysToGrow) * def.stages), + def.stages - 1 + ); + + if (newStage > plot.stage) { + plot.stage = newStage; + this._updateCropSprite(plot); + + if (newStage === def.stages - 1) { + // ZRELA! + this._showFloatingText(plot.x, plot.y - 40, '๐ŸŒŸ Zrelo!', '#ffd700', 24); + this._glowPulse(plot.sprite); + } + } + } + + console.log(`๐ŸŒž Dan ${day} tick โ€” ${this.tilledPlots.filter(p => p.crop).length} crop-ov aktiv`); + } + + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + // UVENJANJE + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + _wiltCrop(plot) { + plot.wilted = true; + + // Sprite potemni in se trese + if (plot.sprite) { + this.scene.tweens.add({ + targets: plot.sprite, + tint: 0x554433, + alpha: 0.5, + duration: 800, + ease: 'Sine.in', + onComplete: () => { + if (plot.sprite) plot.sprite.setTint(0x554433).setAlpha(0.45); + } + }); + } + this._showFloatingText(plot.x, plot.y - 30, '๐Ÿ‚ Uvelo!', '#cc6633'); + console.warn(`๐Ÿ‚ Rastlina uvela @ ${plot.x},${plot.y}`); + } + + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + // POMOลฝNE METODE + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + + _getCropDef(key) { + return this.CROPS[key] || null; + } + + _updateCropSprite(plot) { + const def = this._getCropDef(plot.crop); + const stageKey = def.stageKeys[plot.stage]; + + // Ciljana viลกina: 20px (stage0) โ†’ 70px (zrela) + const minH = 20, maxH = 70; + const cropH = minH + (plot.stage / (def.stages - 1)) * (maxH - minH); + + const texture = this.scene.textures.get(stageKey); + const srcH = texture?.getSourceImage()?.height || 150; + const scale = cropH / srcH; + + if (plot.sprite) { + // Animiran prehod na novo stopnjo + this.scene.tweens.add({ + targets: plot.sprite, + scaleX: { from: plot.sprite.scaleX, to: scale }, + scaleY: { from: plot.sprite.scaleY, to: scale }, + duration: 600, + ease: 'Back.out', + onStart: () => { if (plot.sprite) plot.sprite.setTexture(stageKey); } + }); + } + } + + _removeCrop(plot) { + if (plot.sprite) { plot.sprite.destroy(); plot.sprite = null; } + plot.crop = null; + plot.dayPlanted = null; + plot.dayWatered = null; + plot.stage = 0; + plot.wilted = false; + if (plot.tileSprite) plot.tileSprite.setAlpha(0.9).clearTint(); + } + + _getPlotNear(kai) { + let closest = null; + let minDist = this.interactRange; + for (const plot of this.tilledPlots) { + const d = Phaser.Math.Distance.Between(kai.x, kai.y, plot.x, plot.y); + if (d < minDist) { minDist = d; closest = plot; } + } + return closest; + } + + _getEmptyGroundNear(kai) { + // Snap na tile center โ€” Kai mora biti v bliลพini centra tile-a + const ts = this.tileSize; + const snappedX = Math.floor(kai.x / ts) * ts + ts / 2; + const snappedY = Math.floor(kai.y / ts) * ts + ts / 2; + const dist = Phaser.Math.Distance.Between(kai.x, kai.y, snappedX, snappedY); + if (dist > this.interactRange) return null; + if (this.tilledPlots.some(p => Math.abs(p.x - snappedX) < 8 && Math.abs(p.y - snappedY) < 8)) return null; + return { x: snappedX, y: snappedY }; + } + + _drawTooltip(plot) { + const def = plot.crop ? this._getCropDef(plot.crop) : null; + let label = ''; + if (!plot.crop) label = '๐ŸŒฑ [E] Posadi'; + else if (plot.wilted) label = '๐Ÿ‚ [E] Odstrani'; + else if (plot.stage >= (def?.stages - 1)) label = '๐ŸŒพ [E] Poลพeni!'; + else if (this.activeTool === 'watering_can') label = '๐Ÿ’ง [E] Zalij'; + else label = `๐ŸŒฟ ${plot.crop} (${plot.stage + 1}/${def?.stages})`; + + // Tooltip โ€” svetla, jasna, pri vrhu tilled tile-a + if (!this._tooltipText) { + this._tooltipText = 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 W = 48, H = 24; + this._tooltipText + .setText(label) + .setPosition(plot.x - this._tooltipText.width / 2, plot.y - H / 2 - 20); + } + + _showFloatingText(x, y, msg, color = '#ffffff', fontSize = 20) { + 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 - 60, + alpha: { from: 1, to: 0 }, + duration: 1400, + ease: 'Cubic.out', + onComplete: () => txt.destroy(), + }); + } + + _waterRipple(x, y) { + const gfx = this.scene.add.graphics().setDepth(9997); + let r = 4, alpha = 0.7; + const expand = this.scene.time.addEvent({ + delay: 40, + repeat: 8, + callback: () => { + gfx.clear(); + gfx.lineStyle(2, 0x4499ff, alpha); + gfx.strokeCircle(x, y, r); + r += 6; alpha -= 0.08; + if (r > 55) { gfx.destroy(); expand.remove(); } + } + }); + } + + _glowPulse(sprite) { + if (!sprite) return; + this.scene.tweens.add({ + targets: sprite, + alpha: { from: 1, to: 0.5 }, + duration: 400, + yoyo: true, + repeat: 3, + ease: 'Sine.inOut', + }); + } + + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + // PUBLIC API โ€” Za GrassScene + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + + /** Nastavi aktiven tool (kliฤe InventorySystem) */ + setTool(tool) { this.activeTool = tool; } + + /** Nastavi aktivno seme (kliฤe InventorySystem) */ + setSeed(seed) { this.activeSeed = seed; } + + /** Vrni vse activate plots (za save) */ + getState() { + return this.tilledPlots.map(p => ({ + x: p.x, y: p.y, + crop: p.crop, + dayPlanted: p.dayPlanted, + dayWatered: p.dayWatered, + stage: p.stage, + wilted: p.wilted, + })); + } + + /** Destroy */ + destroy() { + this.highlightGfx?.destroy(); + this._tooltipText?.destroy(); + this.tilledPlots.forEach(p => { + p.tileSprite?.destroy(); + p.sprite?.destroy(); + }); + this.tilledPlots = []; + } +} diff --git a/nova farma TRAE/src/systems/InventorySystem.js b/nova farma TRAE/src/systems/InventorySystem.js new file mode 100644 index 000000000..2f4b56d86 --- /dev/null +++ b/nova farma TRAE/src/systems/InventorySystem.js @@ -0,0 +1,326 @@ +/** + * ============================================================ + * InventorySystem.js โ€” Nova Farma + * ============================================================ + * Hotbar (7 slotov) + Items logika + * + * DEMO starter items: + * Slot 1: ๐Ÿช“ Motika (Hoe) โ†’ ore tla + * Slot 2: ๐Ÿ’ง Zalivalka โ†’ zalij crop + * Slot 3: ๐ŸŒพ Pลกenica semena ร—5 + * Slot 4: ๐Ÿฅ• Korenje semena ร—5 + * Slot 5: โ€” prazen โ€” + * Slot 6: โ€” prazen โ€” + * Slot 7: โ€” prazen โ€” + * + * Keys: [1]-[7] za slot, scroll za rotate + * Emit 'hotbarChanged' { slot, item } โ†’ GrassScene โ†’ FarmingSystem + * ============================================================ + */ +export default class InventorySystem { + + /** + * @param {Phaser.Scene} scene โ€” GrassSceneClean + */ + constructor(scene) { + this.scene = scene; + + // Vsi predmeti (full inventory) + this.items = {}; + // Format: { 'wheat_seed': { name, icon, count, type, tool/seed } } + + // Hotbar = 7 slotov, vsak kaลพe na item key ali null + this.hotbar = [ + 'hoe', + 'watering_can', + 'wheat_seed', + 'carrot_seed', + null, + null, + null, + ]; + this.activeSlot = 0; // Slot 1 = Motika + + // Item definicije + this.ITEM_DEFS = { + hoe: { + name: 'Motika', + icon: '๐Ÿช“', + type: 'tool', + tool: 'hoe', + stackable: false, + count: 1, + desc: '[E] Oraj tla', + }, + watering_can: { + name: 'Zalivalka', + icon: '๐Ÿ’ง', + type: 'tool', + tool: 'watering_can', + stackable: false, + count: 1, + desc: '[E] Zalij rastlino', + }, + wheat_seed: { + name: 'Pลกenica', + icon: '๐ŸŒพ', + type: 'seed', + seed: 'wheat', + stackable: true, + count: 5, + desc: '3 dni โ€ข 30g/harvest', + color: '#ffdd88', + }, + carrot_seed: { + name: 'Korenje', + icon: '๐Ÿฅ•', + type: 'seed', + seed: 'carrot', + stackable: true, + count: 5, + desc: '4 dni โ€ข 40g/harvest', + color: '#ff8844', + }, + cannabis_seed: { + name: 'Konoplja', + icon: '๐ŸŒฟ', + type: 'seed', + seed: 'cannabis', + stackable: true, + count: 0, + desc: '7 dni โ€ข 200g/harvest', + color: '#88cc44', + }, + // Harvested items + wheat: { + name: 'Pลกenica', + icon: '๐ŸŒพ', + type: 'resource', + stackable: true, + count: 0, + value: 30, + }, + carrot: { + name: 'Korenje', + icon: '๐Ÿฅ•', + type: 'resource', + stackable: true, + count: 0, + value: 40, + }, + cannabis_bud: { + name: 'Konoplja', + icon: '๐ŸŒฟ', + type: 'resource', + stackable: true, + count: 0, + value: 200, + }, + }; + + // Inicializiraj items iz ITEM_DEFS (samo tiste s count > 0) + for (const [key, def] of Object.entries(this.ITEM_DEFS)) { + this.items[key] = { ...def }; + } + + // Denar (Gold) + this.gold = 0; + + // Number keys [1]-[7] + for (let i = 1; i <= 7; i++) { + scene.input.keyboard.on(`keydown-${i}`, () => this.selectSlot(i - 1)); + } + + // Scroll wheel za rotation + scene.input.on('wheel', (pointer, gObj, dx, dy) => { + if (dy > 0) this.selectSlot((this.activeSlot + 1) % 7); + else if (dy < 0) this.selectSlot((this.activeSlot + 6) % 7); + }); + + // Posluลกaj harvest evente โ†’ dodaj v inventar + scene.events.on('harvest', (data) => { + this.addItem(data.item, data.count); + this.addGold(data.value); + }); + + // Prviฤ sinhronizira z FarmingSystem + this._syncWithFarming(); + + console.log('๐ŸŽ’ InventorySystem initialized'); + } + + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + // IZBERI SLOT + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + selectSlot(index) { + if (index < 0 || index >= 7) return; + this.activeSlot = index; + this._syncWithFarming(); + this._notifyUI(); + console.log(`๐ŸŽ’ Slot ${index + 1}: ${this.hotbar[index] || 'prazen'}`); + } + + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + // DODAJ ITEM + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + addItem(key, count = 1) { + if (!this.items[key]) { + this.items[key] = { ...(this.ITEM_DEFS[key] || { name: key, icon: '๐Ÿ“ฆ', count: 0 }) }; + } + this.items[key].count += count; + + // Dodaj v hotbar ฤe je prazen slot + seme/resource + const def = this.ITEM_DEFS[key]; + if (def && def.stackable && !this.hotbar.includes(key)) { + const emptySlot = this.hotbar.findIndex(s => s === null); + if (emptySlot !== -1) this.hotbar[emptySlot] = key; + } + + this._notifyUI(); + console.log(`๐ŸŽ’ +${count}ร— ${key} (skupaj: ${this.items[key].count})`); + } + + addGold(amount) { + this.gold += amount; + this._notifyUI(); + console.log(`๐Ÿ’ฐ +${amount}g (skupaj: ${this.gold}g)`); + } + + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + // PORABI ITEM (semena pri sajenju) + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + useActiveItem() { + const key = this.hotbar[this.activeSlot]; + if (!key) return false; + const item = this.items[key]; + if (!item) return false; + + if (item.type === 'seed') { + if (item.count <= 0) { + this._showMsg(`โŒ Ni ${item.name} semen!`, '#ff6666'); + return false; + } + item.count--; + if (item.count <= 0 && item.stackable) { + // Odstrani iz hotbara + this.hotbar[this.activeSlot] = null; + } + this._notifyUI(); + return true; + } + + if (item.type === 'tool') return true; // Orodja se ne porabijo + + return false; + } + + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + // SYNC z FarmingSystem + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + _syncWithFarming() { + const farming = this.scene.farmingSystem; + if (!farming) return; + + const key = this.hotbar[this.activeSlot]; + if (!key) { + farming.setTool(null); + return; + } + + const item = this.items[key]; + if (!item) return; + + if (item.type === 'tool') { + farming.setTool(item.tool); + farming.setSeed(null); + } else if (item.type === 'seed') { + farming.setTool('hoe'); // Semena โ†’ avtomatsko orodje = motika za saditev + farming.setSeed(item.seed); + } else { + farming.setTool(null); + farming.setSeed(null); + } + } + + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + // NOTIFY UIScene + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + _notifyUI() { + const uiScene = this.scene.scene.get('UIScene'); + if (!uiScene) return; + + // Poลกlji hotbar stanje + const hotbarData = this.hotbar.map((key, i) => { + if (!key) return null; + const item = this.items[key]; + const def = this.ITEM_DEFS[key]; + return { + key, + icon: def?.icon || '๐Ÿ“ฆ', + name: def?.name || key, + count: item?.count ?? 1, + active: i === this.activeSlot, + type: def?.type || 'unknown', + stackable: def?.stackable || false, + }; + }); + + uiScene.events.emit('hotbarUpdate', { + slots: hotbarData, + activeSlot: this.activeSlot, + gold: this.gold, + }); + } + + _showMsg(msg, color = '#ffffff') { + const cam = this.scene.cameras.main; + const txt = this.scene.add.text( + cam.worldView.centerX, + cam.worldView.top + 80, + msg, { + fontFamily: 'Arial Black', + fontSize: '20px', + color, + stroke: '#000000', + strokeThickness: 4, + } + ).setOrigin(0.5).setDepth(9999); + + this.scene.tweens.add({ + targets: txt, + y: txt.y - 40, + alpha: { from: 1, to: 0 }, + duration: 1500, + onComplete: () => txt.destroy(), + }); + } + + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + // PUBLIC API + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + getActiveItem() { + const key = this.hotbar[this.activeSlot]; + return key ? this.items[key] : null; + } + + getActiveItemKey() { + return this.hotbar[this.activeSlot]; + } + + getCount(key) { + return this.items[key]?.count || 0; + } + + getGold() { + return this.gold; + } + + getState() { + return { + items: this.items, + hotbar: this.hotbar, + activeSlot: this.activeSlot, + gold: this.gold, + }; + } +} diff --git a/nova farma TRAE/src/systems/WaterSystem.js b/nova farma TRAE/src/systems/WaterSystem.js new file mode 100644 index 000000000..620ce56cc --- /dev/null +++ b/nova farma TRAE/src/systems/WaterSystem.js @@ -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 = []; + } +} diff --git a/nova farma TRAE/src/systems/ZombieSystem.js b/nova farma TRAE/src/systems/ZombieSystem.js new file mode 100644 index 000000000..0c8825704 --- /dev/null +++ b/nova farma TRAE/src/systems/ZombieSystem.js @@ -0,0 +1,466 @@ +/** + * ============================================================ + * ZombieSystem.js โ€” Nova Farma Demo + * ============================================================ + * Shambler zombi AI: + * - Spawn ob robu otoka (ponoฤi) + * - Poฤasi sledi Kai-u (shambling walk) + * - [SPACE] blizu โ†’ Alfa Moฤ โ†’ zombi udomaฤen + * - Udomaฤen zombi sledi Kai-u kot spremljevalec + * - Max 3 aktivni zombiji na otoku + * + * Sprite sheet: zombie_shambler_walk_sheet.png + * Format: 4 stolpci ร— 4 vrstice = 16 frameov + * Frame size: 160ร—160px (640px sheet / 4) + * Vrstice: 0=DOWN, 1=LEFT, 2=RIGHT, 3=UP + * ============================================================ + */ +export default class ZombieSystem { + + constructor(scene, options = {}) { + this.scene = scene; + + // Otok meje (za spawn na robu) + this.islandX = options.islandX || 2000; + this.islandY = options.islandY || 2000; + this.islandW = options.islandW || 6400; + this.islandH = options.islandH || 6400; + + // Zombi parametri + this.maxZombies = 3; + this.spawnAtNight = true; + this.zombieSpeed = 35; // px/s โ€” poฤasi ลกambers + this.detectionRange = 600; // Kai zazna zombija โ†’ prikaลพe opozorilo + this.alfaRange = 120; // Razdalja za Alfa Moฤ [SPACE] + this.tamingTime = 2000; // ms za udomaฤitev (drลพi [SPACE]) + + // Aktivni zombiji + this.zombies = []; + + // Alfa moฤ (SPACE) + this.keySpace = scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.SPACE); + this._alfaProgress = 0; + this._alfaGfx = scene.add.graphics().setDepth(9005); + this._alfaBar = null; + + // Spawn timer + this.spawnTimer = null; + + // Phaser groups + this.zombieGroup = scene.add.group(); + + console.log('๐ŸงŸ ZombieSystem initialized'); + } + + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + // PRELOAD + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + static preload(scene) { + // Frame size: 640/4 = 160px + scene.load.spritesheet('zombie_shambler', 'DEMO_FAZA1/Characters/zombie_shambler_walk_sheet.png', { + frameWidth: 160, + frameHeight: 160, + }); + } + + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + // CREATE ANIMATIONS + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + createAnimations() { + const fps = 6; + const anims = [ + { key: 'zombie_walk_down', frames: [0, 1, 2, 3] }, + { key: 'zombie_walk_left', frames: [4, 5, 6, 7] }, + { key: 'zombie_walk_right', frames: [8, 9, 10, 11] }, + { key: 'zombie_walk_up', frames: [12, 13, 14, 15] }, + ]; + + for (const anim of anims) { + if (this.scene.anims.exists(anim.key)) continue; + this.scene.anims.create({ + key: anim.key, + frames: this.scene.anims.generateFrameNumbers('zombie_shambler', { + frames: anim.frames, + }), + frameRate: fps, + repeat: -1, + }); + } + console.log('๐ŸงŸ Zombie animations created'); + } + + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + // SPAWN TIMER โ€” Zaลพeni ob noฤ eventa + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + startNightSpawning() { + if (this.spawnTimer) return; + this.spawnTimer = this.scene.time.addEvent({ + delay: 12000, // Vsakih 12s poskusi spawnat (ponoฤi) + loop: true, + callback: () => { + if (this.zombies.length < this.maxZombies) { + this.spawnZombie(); + } + } + }); + // Takoj spawni prvega + this.scene.time.delayedCall(2000, () => { + if (this.zombies.length < this.maxZombies) this.spawnZombie(); + }); + console.log('๐ŸŒ™ Zombiji se zaฤnejo spawnat!'); + } + + stopNightSpawning() { + if (this.spawnTimer) { + this.spawnTimer.remove(); + this.spawnTimer = null; + } + } + + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + // SPAWN POSAMIฤŒNEGA ZOMBIJA + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + spawnZombie(x = null, y = null) { + // Spawn na nakljuฤnem robu otoka + if (x === null || y === null) { + const edge = Phaser.Math.Between(0, 3); + const margin = 200; + switch (edge) { + case 0: // Zgornji rob + x = this.islandX + Phaser.Math.Between(margin, this.islandW - margin); + y = this.islandY + margin; + break; + case 1: // Desni rob + x = this.islandX + this.islandW - margin; + y = this.islandY + Phaser.Math.Between(margin, this.islandH - margin); + break; + case 2: // Spodnji rob + x = this.islandX + Phaser.Math.Between(margin, this.islandW - margin); + y = this.islandY + this.islandH - margin; + break; + case 3: // Levi rob + x = this.islandX + margin; + y = this.islandY + Phaser.Math.Between(margin, this.islandH - margin); + break; + } + } + + const sprite = this.scene.add.sprite(x, y, 'zombie_shambler', 0) + .setScale(0.55) // 160px ร— 0.55 โ‰ˆ 88px โ€” primerljivo s Kai-jem + .setOrigin(0.5, 1.0) + .setDepth(y); + + // Pop-in z mgle + sprite.setAlpha(0); + this.scene.tweens.add({ + targets: sprite, + alpha: 1, + duration: 800, + ease: 'Sine.in', + }); + + sprite.play('zombie_walk_down'); + + // Rdeฤ halo efekt + const halo = this.scene.add.graphics().setDepth(y - 1); + halo.fillStyle(0xff0000, 0.15); + halo.fillCircle(x, y - 20, 35); + + const zombie = { + sprite, + halo, + x, y, + state: 'wandering', // 'wandering' | 'chasing' | 'tamed' + tamed: false, + hp: 30, + maxHp: 30, + dir: 'down', + wanderTimer: 0, + wanderDirX: 0, + wanderDirY: 1, + }; + + this.zombies.push(zombie); + console.log(`๐ŸงŸ Zombi spawnan @ ${Math.round(x)},${Math.round(y)}. Skupaj: ${this.zombies.length}`); + return zombie; + } + + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + // UPDATE โ€” Kliฤi vsak frame iz scene.update() + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + update(kai, delta) { + this._alfaGfx.clear(); + + const nearestHostile = this._getNearestHostile(kai); + + // === ALFA MOฤŒ [SPACE] === + if (nearestHostile && Phaser.Math.Distance.Between( + kai.x, kai.y, nearestHostile.sprite.x, nearestHostile.sprite.y + ) < this.alfaRange) { + + if (this.keySpace.isDown) { + this._alfaProgress += delta; + this._drawAlfaBar(kai, nearestHostile); + + if (this._alfaProgress >= this.tamingTime) { + this._tameZombie(nearestHostile, kai); + this._alfaProgress = 0; + } + } else { + this._alfaProgress = Math.max(0, this.alfaProgress - delta * 2); + } + + // Hint tekst + if (!this._spaceHint || !this._spaceHint.active) { + this._spaceHint = this._showHint( + nearestHostile.sprite.x, + nearestHostile.sprite.y - 80, + '[SPACE] Alfa Moฤ!', '#ff4444' + ); + } + } else { + this._alfaProgress = 0; + if (this._spaceHint) { this._spaceHint.destroy(); this._spaceHint = null; } + } + + // === POSODOBI VSE ZOMBIJE === + for (let i = this.zombies.length - 1; i >= 0; i--) { + const z = this.zombies[i]; + if (!z.sprite || !z.sprite.active) { + this.zombies.splice(i, 1); + continue; + } + + this._updateZombie(z, kai, delta); + } + } + + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + // POSODOBI POSAMIฤŒNEGA ZOMBIJA + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + _updateZombie(z, kai, delta) { + const dt = delta / 1000; + const speed = z.tamed ? this.zombieSpeed * 1.3 : this.zombieSpeed; + + const distToKai = Phaser.Math.Distance.Between( + z.sprite.x, z.sprite.y, kai.x, kai.y + ); + + let moveX = 0, moveY = 0; + + if (z.tamed) { + // === UDOMAฤŒEN: sledi Kai-u na razdalji 80px === + if (distToKai > 80) { + const angle = Phaser.Math.Angle.Between(z.sprite.x, z.sprite.y, kai.x, kai.y); + moveX = Math.cos(angle) * speed * dt; + moveY = Math.sin(angle) * speed * dt; + } + // Zeleni halo za udomaฤene + z.halo.clear(); + z.halo.fillStyle(0x44ff88, 0.2); + z.halo.fillCircle(z.sprite.x, z.sprite.y - 20, 35); + + } else if (distToKai < 450) { + // === ZAZNALI KAI-A: Lovijo === + z.state = 'chasing'; + const angle = Phaser.Math.Angle.Between(z.sprite.x, z.sprite.y, kai.x, kai.y); + moveX = Math.cos(angle) * speed * dt; + moveY = Math.sin(angle) * speed * dt; + + // Rdeฤi halo (nevarnost) + z.halo.clear(); + z.halo.fillStyle(0xff0000, 0.18); + z.halo.fillCircle(z.sprite.x, z.sprite.y - 20, 40); + + } else { + // === TAVANJE === + z.state = 'wandering'; + z.wanderTimer -= delta; + if (z.wanderTimer <= 0) { + const angle = Math.random() * Math.PI * 2; + z.wanderDirX = Math.cos(angle); + z.wanderDirY = Math.sin(angle); + z.wanderTimer = 2000 + Math.random() * 3000; + } + moveX = z.wanderDirX * speed * 0.5 * dt; + moveY = z.wanderDirY * speed * 0.5 * dt; + + // Subtilen rdeฤi halo + z.halo.clear(); + z.halo.fillStyle(0xff2200, 0.08); + z.halo.fillCircle(z.sprite.x, z.sprite.y - 20, 30); + } + + // === PREMAKNI === + z.sprite.x += moveX; + z.sprite.y += moveY; + z.halo.x = z.sprite.x - (z.halo.x || 0); + + // Depth sorting + z.sprite.setDepth(z.sprite.y); + + // === ANIMACIJA po smeri === + const absX = Math.abs(moveX), absY = Math.abs(moveY); + let newDir = z.dir; + + if (absX > 0.01 || absY > 0.01) { + if (absY > absX) { + newDir = moveY > 0 ? 'down' : 'up'; + } else { + newDir = moveX > 0 ? 'right' : 'left'; + } + } + + if (newDir !== z.dir) { + z.dir = newDir; + const animKey = `zombie_walk_${newDir}`; + if (z.sprite.anims.currentAnim?.key !== animKey) { + z.sprite.play(animKey, true); + } + } + + // Otok bounds + z.sprite.x = Phaser.Math.Clamp(z.sprite.x, this.islandX, this.islandX + this.islandW); + z.sprite.y = Phaser.Math.Clamp(z.sprite.y, this.islandY, this.islandY + this.islandH); + } + + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + // ALFA MOฤŒ โ€” Udomaฤitev + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + _tameZombie(zombie, kai) { + zombie.tamed = true; + zombie.state = 'tamed'; + + // Efekt: zlata eksplozija + this._alfaExplosion(zombie.sprite.x, zombie.sprite.y); + + // Obarvi v zlato (shimmer) + zombie.sprite.setTint(0xffd700); + this.scene.time.delayedCall(800, () => { + zombie.sprite.clearTint(); + }); + + // Notify scene + this.scene.events.emit('zombieTamed', { zombie }); + + this._showFloating(zombie.sprite.x, zombie.sprite.y - 60, + 'โœจ Udomaฤen!', '#ffd700', 22); + + console.log(`โœจ Zombi udomaฤen!`); + } + + _drawAlfaBar(kai, zombie) { + const pct = this._alfaProgress / this.tamingTime; + const bx = zombie.sprite.x - 40; + const by = zombie.sprite.y - 110; + + // Ozadje + this._alfaGfx.fillStyle(0x000000, 0.6); + this._alfaGfx.fillRect(bx, by, 80, 12); + + // Fill + this._alfaGfx.fillStyle(0xff4444, 1.0); + this._alfaGfx.fillRect(bx + 1, by + 1, (80 - 2) * pct, 10); + + // Okvir + this._alfaGfx.lineStyle(1, 0xffffff, 0.8); + this._alfaGfx.strokeRect(bx, by, 80, 12); + } + + _alfaExplosion(x, y) { + const gfx = this.scene.add.graphics().setDepth(9010); + let r = 5, alpha = 1.0; + const ev = this.scene.time.addEvent({ + delay: 30, + repeat: 15, + callback: () => { + gfx.clear(); + gfx.lineStyle(3, 0xffd700, alpha); + gfx.strokeCircle(x, y - 30, r); + gfx.strokeCircle(x, y - 30, r * 0.6); + r += 8; alpha -= 0.06; + if (r > 100) { gfx.destroy(); ev.remove(); } + } + }); + + // Zlatni prah + for (let i = 0; i < 8; i++) { + const angle = (i / 8) * Math.PI * 2; + const particle = this.scene.add.graphics().setDepth(9011); + particle.fillStyle(0xffd700, 1.0); + particle.fillCircle(x, y - 30, 4); + + this.scene.tweens.add({ + targets: particle, + x: particle.x + Math.cos(angle) * 60, + y: particle.y + Math.sin(angle) * 60, + alpha: 0, + duration: 600, + ease: 'Quad.out', + onComplete: () => particle.destroy(), + }); + } + } + + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + // POMOลฝNE + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + _getNearestHostile(kai) { + let nearest = null, minDist = this.alfaRange * 1.5; + for (const z of this.zombies) { + if (z.tamed) continue; + const d = Phaser.Math.Distance.Between(kai.x, kai.y, z.sprite.x, z.sprite.y); + if (d < minDist) { minDist = d; nearest = z; } + } + return nearest; + } + + _showHint(x, y, msg, color) { + return this.scene.add.text(x, y, msg, { + fontFamily: 'Arial Black', + fontSize: '14px', + color, + stroke: '#000000', + strokeThickness: 4, + backgroundColor: '#00000088', + padding: { x: 6, y: 3 }, + }).setOrigin(0.5).setDepth(9000); + } + + _showFloating(x, y, msg, color, size = 20) { + const txt = this.scene.add.text(x, y, msg, { + fontFamily: 'Arial Black', + fontSize: `${size}px`, + color, + stroke: '#000000', + strokeThickness: 4, + }).setOrigin(0.5).setDepth(9999); + + this.scene.tweens.add({ + targets: txt, + y: y - 60, + alpha: { from: 1, to: 0 }, + duration: 1800, + onComplete: () => txt.destroy(), + }); + } + + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + // PUBLIC API + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + getZombies() { return this.zombies; } + getTamedZombies() { return this.zombies.filter(z => z.tamed); } + getHostiles() { return this.zombies.filter(z => !z.tamed); } + + removeAll() { + this.zombies.forEach(z => { z.sprite?.destroy(); z.halo?.destroy(); }); + this.zombies = []; + } + + destroy() { + this.removeAll(); + this.stopNightSpawning(); + this._alfaGfx?.destroy(); + this._spaceHint?.destroy(); + } +}