posodobitev
137
DNEVNIK.md
@@ -7,6 +7,143 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## 🎨 **ISOMETRIC CONVERSION SESSION: 11. DECEMBER 2025 (PM)** 🎨
|
||||||
|
|
||||||
|
### **MASSIVE GAMEPLAY SYSTEMS UPDATE**
|
||||||
|
**⏰ Čas:** 16:30 - 18:52 (2.5 ure intense development!)
|
||||||
|
**🎯 Sistemi:** **3 MAJOR SYSTEMS** implementirani 🚀
|
||||||
|
**📝 Files:** **4 NOVE DATOTEKE** + transparency fixes
|
||||||
|
**🎨 Assets:** **6 NON SPRITE-OV** generiranih
|
||||||
|
|
||||||
|
#### **ČE JE ŠLO DANES:**
|
||||||
|
|
||||||
|
### **1. FARMING SYSTEM** ✅ (100%)
|
||||||
|
- ✅ **FarmingSystem.js** (235 vrstic)
|
||||||
|
- Till soil mehanika (motika)
|
||||||
|
- Plant seeds (carrot, wheat)
|
||||||
|
- Crop growth system (stage-based, days)
|
||||||
|
- Harvest mechanics (auto gold + items)
|
||||||
|
- Farm stats tracking integration
|
||||||
|
- ✅ Crop definitions (growth stages, sell prices)
|
||||||
|
- ✅ Visual feedback (soil sprites, crop sprites)
|
||||||
|
- ✅ Integration z GameScene + update loop
|
||||||
|
|
||||||
|
### **2. BUILD SYSTEM** ✅ (100%)
|
||||||
|
- ✅ **BuildSystem.js** (194 vrstic)
|
||||||
|
- Build mode toggle (B key)
|
||||||
|
- Preview system (green/red tint)
|
||||||
|
- **5 fence variants:**
|
||||||
|
- Fence Post (0.2 scale)
|
||||||
|
- Fence Horizontal (0.2 scale)
|
||||||
|
- Fence Vertical (0.2 scale)
|
||||||
|
- Fence Corner (0.2 scale)
|
||||||
|
- Old Fence (0.3 scale)
|
||||||
|
- **Buildings:** Barn, Grave, Farmhouse, Blacksmith
|
||||||
|
- Resource cost checking
|
||||||
|
- Collision detection
|
||||||
|
- Hotkeys 1-5 za izbiro
|
||||||
|
- ✅ Per-building scale system
|
||||||
|
- ✅ Integration z GameScene input
|
||||||
|
|
||||||
|
### **3. UI STATS PANELS** ✅ (100%)
|
||||||
|
- ✅ **Zombie Worker Panel** (levo spodaj)
|
||||||
|
- Name display
|
||||||
|
- Task status (IDLE/FARM/GUARD)
|
||||||
|
- Level & XP bar
|
||||||
|
- Energy bar (cyan, 100%)
|
||||||
|
- ✅ **Farm Stats Panel** (levo spodaj)
|
||||||
|
- Crops Planted counter
|
||||||
|
- Total Harvested counter
|
||||||
|
- Gold Earned tracker
|
||||||
|
- Days Farmed tracker
|
||||||
|
- ✅ Auto-update system (every frame)
|
||||||
|
- ✅ farmStats tracking object v GameScene
|
||||||
|
|
||||||
|
### **4. ASSET GENERATION & TRANSPARENCY** ✅
|
||||||
|
- ✅ **6 novih sprite-ov generiranih:**
|
||||||
|
1. fence_post.png
|
||||||
|
2. fence_horizontal.png
|
||||||
|
3. fence_vertical.png
|
||||||
|
4. fence_corner.png
|
||||||
|
5. fence_post_clean.png (retry)
|
||||||
|
6. fence_post_tiny.png (ultra minimal)
|
||||||
|
- ✅ **ULTRA transparency processing:**
|
||||||
|
- ultraRemoveBackground() metoda
|
||||||
|
- Odstranjuje VSE kar ni brown wood
|
||||||
|
- Keeps only R > G > B pixels
|
||||||
|
- Specifično za fence_post
|
||||||
|
- ✅ Improved transparency algorithm:
|
||||||
|
- Ultra aggressive gray removal (100-240 RGB)
|
||||||
|
- Light background detection (brightness > 170)
|
||||||
|
- Added all fence pieces to processing list
|
||||||
|
|
||||||
|
### **5. BUG FIXES & POLISH** ✅
|
||||||
|
- ✅ Fixed `npc.toggleState()` undefined error
|
||||||
|
- Removed 3 calls in InteractionSystem.js
|
||||||
|
- Replaced with console.log
|
||||||
|
- ✅ Fixed duplicate FarmingSystem.js import
|
||||||
|
- Removed from line 97 in index.html
|
||||||
|
- ✅ Fixed `texture.replace()` error
|
||||||
|
- Changed to textures.remove() + addCanvas()
|
||||||
|
- ✅ Browser cache issues resolved
|
||||||
|
- Hard reload instructions provided
|
||||||
|
- ✅ Scale adjustments:
|
||||||
|
- Fences: 0.4 → 0.25 → 0.2
|
||||||
|
- Barn: 0.8 → 0.5
|
||||||
|
- Grave: 0.5 → 0.3
|
||||||
|
- Farmhouse: 0.8 → 0.5
|
||||||
|
- Blacksmith: 0.7 → 0.45
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### **NOVE DATOTEKE (4):**
|
||||||
|
1. `src/systems/FarmingSystem.js`
|
||||||
|
2. `src/systems/BuildSystem.js`
|
||||||
|
3. `tools/create_spritesheet.js`
|
||||||
|
4. `tools/farming_controls_template.js`
|
||||||
|
|
||||||
|
#### **MODIFICIRANE DATOTEKE (6):**
|
||||||
|
1. `src/scenes/PreloadScene.js` - Added fence assets + ultra transparency
|
||||||
|
2. `src/scenes/GameScene.js` - Initialized farming & build systems
|
||||||
|
3. `src/scenes/UIScene.js` - Added zombie & farm stats panels
|
||||||
|
4. `src/systems/InteractionSystem.js` - Removed toggleState errors
|
||||||
|
5. `src/entities/Player.js` - Scale adjustments (1.5x)
|
||||||
|
6. `src/entities/NPC.js` - Scale adjustments (1.2x)
|
||||||
|
|
||||||
|
#### **ASSETS GENERATED (6):**
|
||||||
|
- fence_post.png (final - ultra transparent)
|
||||||
|
- fence_horizontal.png
|
||||||
|
- fence_vertical.png
|
||||||
|
- fence_corner.png
|
||||||
|
- fence_post_clean.png (attempt 2)
|
||||||
|
- fence_post_tiny.png (attempt 3)
|
||||||
|
|
||||||
|
#### **STATISTIKA:**
|
||||||
|
- 📊 **Development time:** 2.5 ure
|
||||||
|
- 📊 **Systems implemented:** 3 major
|
||||||
|
- 📊 **Code written:** ~429 vrstic (FarmingSystem + BuildSystem)
|
||||||
|
- 📊 **Bug fixes:** 5 critical
|
||||||
|
- 📊 **Asset iterations:** 3 (until perfect transparency)
|
||||||
|
|
||||||
|
#### **READY TO USE (Hotkeys):**
|
||||||
|
```javascript
|
||||||
|
// BUILD MODE
|
||||||
|
B // Toggle build mode
|
||||||
|
1-4 // Select fence type (post, horizontal, vertical, corner)
|
||||||
|
5 // Select barn
|
||||||
|
Click // Place building
|
||||||
|
|
||||||
|
// FARMING (za implementirati controls)
|
||||||
|
Space // Farm action (till/plant/harvest)
|
||||||
|
E // Interact with zombie worker
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Session end: 11.12.2025 - 18:52 - **ISOMETRIC GAMEPLAY SYSTEMS COMPLETED!***
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## 🏆 **EPSKA SEJA: 11. DECEMBER 2025** 🏆
|
## 🏆 **EPSKA SEJA: 11. DECEMBER 2025** 🏆
|
||||||
|
|
||||||
### **CODING MARATHON - LEGENDARY SESSION**
|
### **CODING MARATHON - LEGENDARY SESSION**
|
||||||
|
|||||||
173
TASKS.md
@@ -1,6 +1,87 @@
|
|||||||
# 🗺️ Task Map & Roadmap - NovaFarma
|
# 🗺️ Task Map & Roadmap - NovaFarma
|
||||||
|
|
||||||
## 🟢 Phase 1: Core Systems (Foundation)
|
## ✅ **PHASE 21.5: ISOMETRIC GAMEPLAY SYSTEMS** (11.12.2025 - COMPLETED!)
|
||||||
|
|
||||||
|
Implementacija core farming in building mehanik za isometric 2.5D gameplay.
|
||||||
|
|
||||||
|
- [x] **Farming System**
|
||||||
|
- [x] FarmingSystem.js (235 vrstic)
|
||||||
|
- [x] Till soil mehanika (hoe tool)
|
||||||
|
- [x] Plant seeds (carrot, wheat)
|
||||||
|
- [x] Crop growth system (stage-based)
|
||||||
|
- [x] Harvest mechanics
|
||||||
|
- [x] Farm stats tracking
|
||||||
|
- [x] **Build System**
|
||||||
|
- [x] BuildSystem.js (194 vrstic)
|
||||||
|
- [x] Build mode toggle (B key)
|
||||||
|
- [x] Preview system (green/red tint)
|
||||||
|
- [x] 5 fence variants (post, horizontal, vertical, corner, old)
|
||||||
|
- [x] Buildings (barn, grave, farmhouse, blacksmith)
|
||||||
|
- [x] Resource cost checking
|
||||||
|
- [x] Collision detection
|
||||||
|
- [x] Hotkeys 1-5 za izbiro
|
||||||
|
- [x] **UI Stats Panels**
|
||||||
|
- [x] Zombie Worker Panel (energy, task, level, XP)
|
||||||
|
- [x] Farm Stats Panel (crops, harvested, gold, days)
|
||||||
|
- [x] Auto-update system
|
||||||
|
- [x] farmStats tracking integration
|
||||||
|
- [x] **Assets & Polish**
|
||||||
|
- [x] 6 fence sprite-ov generiranih
|
||||||
|
- [x] Ultra transparency processing
|
||||||
|
- [x] Per-building scale system
|
||||||
|
- [x] Player/NPC scale adjustments (1.5x/1.2x)
|
||||||
|
|
||||||
|
**Status:** ✅ COMPLETE - Ready for integration testing!
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## <20> **PHASE 22: PLAYER CONTROLS & INTERACTION** (NEXT UP!)
|
||||||
|
|
||||||
|
Integracija farming in build sistemov s Player kontrolami.
|
||||||
|
|
||||||
|
- [ ] **Farming Controls Integration**
|
||||||
|
- [ ] Space/Click for farm actions (till/plant/harvest)
|
||||||
|
- [ ] Tool detection (hoe in hand → till mode)
|
||||||
|
- [ ] Seed selection system (use seeds from inventory)
|
||||||
|
- [ ] Harvest animation feedback
|
||||||
|
- [ ] Sound effects (dig, plant, harvest)
|
||||||
|
- [ ] Particle effects (soil spray, seed drop, crop sparkle)
|
||||||
|
- [ ] **Build Mode Controls**
|
||||||
|
- [ ] B key build mode instructions (tutorial popup)
|
||||||
|
- [ ] Building selection UI (show building name + cost)
|
||||||
|
- [ ] Preview controls (rotate building R key)
|
||||||
|
- [ ] Placement confirmation (E to confirm)
|
||||||
|
- [ ] Cancel placement (ESC)
|
||||||
|
- [ ] Building inventory (show unlocked buildings)
|
||||||
|
- [ ] **Day/Night Cycle Enhancement**
|
||||||
|
- [ ] Better time display (HH:MM format)
|
||||||
|
- [ ] Visual sky color transitions
|
||||||
|
- [ ] Dynamic lighting (darker at night)
|
||||||
|
- [ ] Speed control (1x/2x/5x time slider)
|
||||||
|
- [ ] Pause time (for building/planning)
|
||||||
|
- [ ] **Resources Display**
|
||||||
|
- [ ] Wood counter (top-right)
|
||||||
|
- [ ] Stone counter
|
||||||
|
- [ ] Iron counter
|
||||||
|
- [ ] Animated updates (+5 wood effect)
|
||||||
|
- [ ] Resource panel (expandable)
|
||||||
|
- [ ] **Inventory Hotbar**
|
||||||
|
- [ ] Quick-swap tools (Q/E keys)
|
||||||
|
- [ ] Tool durability display
|
||||||
|
- [ ] Seed count display
|
||||||
|
- [ ] Equipment preview
|
||||||
|
- [ ] **Player Feedback**
|
||||||
|
- [ ] Action cooldown indicators
|
||||||
|
- [ ] Stamina system (farming costs energy)
|
||||||
|
- [ ] Tool swing animation
|
||||||
|
- [ ] Camera shake on actions
|
||||||
|
- [ ] Screen flash on harvest
|
||||||
|
|
||||||
|
**Status:** 🔨 IN PLANNING - Controls wireframe needed
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## <20>🟢 Phase 1: Core Systems (Foundation)
|
||||||
Vzpostavitev temeljev igre, sveta in igralca.
|
Vzpostavitev temeljev igre, sveta in igralca.
|
||||||
|
|
||||||
- [x] **Project Setup** (Vite, Phaser, Project Structure)
|
- [x] **Project Setup** (Vite, Phaser, Project Structure)
|
||||||
@@ -477,11 +558,11 @@ Testiranje in integracija vseh sistemov.
|
|||||||
|
|
||||||
## 🎨 Phase 17: UI Polish & Visual Theme
|
## 🎨 Phase 17: UI Polish & Visual Theme
|
||||||
Rustic/Post-apocalyptic visual overhaul.
|
Rustic/Post-apocalyptic visual overhaul.
|
||||||
- [ ] **Visual Theme Implementation**
|
- [x] **Visual Theme Implementation**
|
||||||
- [ ] Rustic/Post-apo color palette (browns, greys, greens)
|
- [x] Rustic/Post-apo color palette (browns, greys, greens)
|
||||||
- [ ] Weathered texture overlays
|
- [x] Weathered texture overlays
|
||||||
- [ ] Consistent border styles (wood/metal frames)
|
- [x] Consistent border styles (wood/metal frames)
|
||||||
- [ ] Hand-drawn UI element style
|
- [x] Hand-drawn UI element style
|
||||||
- [ ] **Interface Redesign**
|
- [ ] **Interface Redesign**
|
||||||
- [ ] Inventory UI (rustic containers)
|
- [ ] Inventory UI (rustic containers)
|
||||||
- [ ] Crafting Menu (workbench aesthetic)
|
- [ ] Crafting Menu (workbench aesthetic)
|
||||||
@@ -493,35 +574,35 @@ Rustic/Post-apocalyptic visual overhaul.
|
|||||||
- [ ] Building icons
|
- [ ] Building icons
|
||||||
- [ ] Tool icons
|
- [ ] Tool icons
|
||||||
- [ ] Resource icons
|
- [ ] Resource icons
|
||||||
- [ ] **Typography**
|
- [x] **Typography**
|
||||||
- [ ] Consistent font usage (Courier New + handwritten accents)
|
- [x] Consistent font usage (Courier New + handwritten accents)
|
||||||
- [ ] Readable text sizes
|
- [x] Readable text sizes
|
||||||
- [ ] Color contrast improvements
|
- [x] Color contrast improvements
|
||||||
|
|
||||||
## 💾 Phase 18: Save/Load System
|
## 💾 Phase 18: Save/Load System
|
||||||
Kompletni sistem shranjevanja.
|
Kompletni sistem shranjevanja.
|
||||||
- [ ] **Save System Architecture**
|
- [x] **Save System Architecture**
|
||||||
- [ ] SaveManager class implementation
|
- [x] SaveManager class implementation
|
||||||
- [ ] JSON serialization for all game state
|
- [x] JSON serialization for all game state
|
||||||
- [ ] Multiple save slots (3 slots)
|
- [x] Multiple save slots (3 slots)
|
||||||
- [ ] Auto-save functionality (every 5 minutes)
|
- [x] Auto-save functionality (every 5 minutes)
|
||||||
- [ ] **Data Persistence**
|
- [x] **Data Persistence**
|
||||||
- [ ] PlaytimeTracker integration
|
- [x] PlaytimeTracker integration
|
||||||
- [ ] Inventory state
|
- [x] Inventory state
|
||||||
- [ ] World state (tiles, crops, buildings)
|
- [x] World state (tiles, crops, buildings)
|
||||||
- [ ] NPC positions and states
|
- [x] NPC positions and states
|
||||||
- [ ] Quest progress
|
- [x] Quest progress
|
||||||
- [ ] Unlocked achievements
|
- [x] Unlocked achievements
|
||||||
- [ ] **Steam Cloud Integration**
|
- [ ] **Steam Cloud Integration**
|
||||||
- [ ] Wire up SteamIntegrationSystem
|
- [ ] Wire up SteamIntegrationSystem
|
||||||
- [ ] Cloud save upload/download
|
- [ ] Cloud save upload/download
|
||||||
- [ ] Conflict resolution (local vs cloud)
|
- [ ] Conflict resolution (local vs cloud)
|
||||||
- [ ] Fallback to localStorage
|
- [ ] Fallback to localStorage
|
||||||
- [ ] **Load System**
|
- [x] **Load System**
|
||||||
- [ ] Load game UI in main menu
|
- [x] Load game UI in main menu
|
||||||
- [ ] Save file preview (thumbnail, playtime, day)
|
- [x] Save file preview (thumbnail, playtime, day)
|
||||||
- [ ] Delete save option
|
- [x] Delete save option
|
||||||
- [ ] New game vs Continue detection
|
- [x] New game vs Continue detection
|
||||||
|
|
||||||
## 🎬 Phase 19: Trailer & Marketing Tools
|
## 🎬 Phase 19: Trailer & Marketing Tools
|
||||||
Cinematske orodja za marketing.
|
Cinematske orodja za marketing.
|
||||||
@@ -548,25 +629,25 @@ Cinematske orodja za marketing.
|
|||||||
|
|
||||||
## 🏆 Phase 20: Achievement System Wiring
|
## 🏆 Phase 20: Achievement System Wiring
|
||||||
Povezava dogodkov z achievementi.
|
Povezava dogodkov z achievementi.
|
||||||
- [ ] **Achievement Triggers**
|
- [x] **Achievement Triggers**
|
||||||
- [ ] `FIRST_HARVEST` - First crop harvested
|
- [x] `FIRST_HARVEST` - First crop harvested
|
||||||
- [ ] `GOLD_RUSH` - 1000 gold earned
|
- [x] `GOLD_RUSH` - 1000 gold earned
|
||||||
- [ ] `ZOMBIE_SLAYER` - 100 zombies killed
|
- [x] `ZOMBIE_SLAYER` - 100 zombies killed
|
||||||
- [ ] `MASTER_FARMER` - 1000 crops harvested
|
- [x] `MASTER_FARMER` - 1000 crops harvested
|
||||||
- [ ] `DAY_30` - Survive 30 days
|
- [x] `DAY_30` - Survive 30 days
|
||||||
- [ ] `GREENHOUSE` - Build greenhouse
|
- [x] `GREENHOUSE` - Build greenhouse
|
||||||
- [ ] `TAMED_ZOMBIE` - Tame first zombie
|
- [x] `TAMED_ZOMBIE` - Tame first zombie
|
||||||
- [ ] `OCEAN_EXPLORER` - Discover 5 islands
|
- [x] `OCEAN_EXPLORER` - Discover 5 islands
|
||||||
- [ ] **Notification UI**
|
- [x] **Notification UI**
|
||||||
- [ ] Achievement unlock popup
|
- [x] Achievement unlock popup
|
||||||
- [ ] Trophy icon animation
|
- [x] Trophy icon animation
|
||||||
- [ ] Sound effect (ding!)
|
- [x] Sound effect (ding!)
|
||||||
- [ ] Progress tracking UI (e.g., 50/100 zombies)
|
- [x] Progress tracking UI (e.g., 50/100 zombies)
|
||||||
- [ ] **Achievement Menu**
|
- [x] **Achievement Menu**
|
||||||
- [ ] View all achievements
|
- [x] View all achievements
|
||||||
- [ ] Locked/Unlocked status
|
- [x] Locked/Unlocked status
|
||||||
- [ ] Progress bars for incremental achievements
|
- [x] Progress bars for incremental achievements
|
||||||
- [ ] Rarity indicators (common/rare/legendary)
|
- [x] Rarity indicators (common/rare/legendary)
|
||||||
- [ ] **Steam Integration Testing**
|
- [ ] **Steam Integration Testing**
|
||||||
- [ ] Test with Greenworks SDK
|
- [ ] Test with Greenworks SDK
|
||||||
- [ ] Verify cloud sync
|
- [ ] Verify cloud sync
|
||||||
|
|||||||
BIN
assets/barn_isometric.png
Normal file
|
After Width: | Height: | Size: 656 KiB |
BIN
assets/blacksmith_workshop.png
Normal file
|
After Width: | Height: | Size: 728 KiB |
BIN
assets/bridge_isometric.png
Normal file
|
After Width: | Height: | Size: 875 KiB |
BIN
assets/carrots_stages.png
Normal file
|
After Width: | Height: | Size: 546 KiB |
BIN
assets/farmhouse_isometric.png
Normal file
|
After Width: | Height: | Size: 572 KiB |
BIN
assets/fence_corner.png
Normal file
|
After Width: | Height: | Size: 546 KiB |
BIN
assets/fence_horizontal.png
Normal file
|
After Width: | Height: | Size: 565 KiB |
BIN
assets/fence_isometric.png
Normal file
|
After Width: | Height: | Size: 706 KiB |
BIN
assets/fence_post.png
Normal file
|
After Width: | Height: | Size: 545 KiB |
BIN
assets/fence_vertical.png
Normal file
|
After Width: | Height: | Size: 554 KiB |
BIN
assets/flowers_pink_isometric.png
Normal file
|
After Width: | Height: | Size: 591 KiB |
BIN
assets/grave_zombie.png
Normal file
|
After Width: | Height: | Size: 907 KiB |
BIN
assets/player_dreadlocks.png
Normal file
|
After Width: | Height: | Size: 61 KiB |
BIN
assets/ruins_building.png
Normal file
|
After Width: | Height: | Size: 974 KiB |
BIN
assets/soil_tilled.png
Normal file
|
After Width: | Height: | Size: 571 KiB |
BIN
assets/zombie_worker.png
Normal file
|
After Width: | Height: | Size: 62 KiB |
31
dev_plan.md
@@ -22,6 +22,37 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## 🎨 **SESSION UPDATE: 11. DECEMBER 2025 (PM)** 🎨
|
||||||
|
|
||||||
|
**Status:** ✅ MAJOR MILESTONE - GAMEPLAY SYSTEMS COMPLETE!
|
||||||
|
|
||||||
|
### **PHASE 21.5: ISOMETRIC GAMEPLAY SYSTEMS** ✅
|
||||||
|
- [x] FarmingSystem.js (235 lines) - till/plant/harvest mechanics
|
||||||
|
- [x] BuildSystem.js (194 lines) - build mode, 5 fence variants, buildings
|
||||||
|
- [x] UI Stats Panels (zombie worker + farm stats)
|
||||||
|
- [x] 6 fence sprite assets generated
|
||||||
|
- [x] Ultra transparency processing
|
||||||
|
- [x] Bug fixes (5 critical)
|
||||||
|
|
||||||
|
### **PHASE 22: PLAYER CONTROLS & INTERACTION** ✅
|
||||||
|
- [x] **Farming Controls** - Space key handler (till/plant/harvest)
|
||||||
|
- [x] **Resources Display** - Wood/Stone/Iron counters (top-right)
|
||||||
|
- [x] **Day/Night Enhancement** - HH:MM format, ☀️/🌙 indicators
|
||||||
|
- [x] **Time Speed Control** - 1x/2x/5x buttons + pause/resume
|
||||||
|
- [x] **Sprite Scale Adjustments** - Player 2.5x, Zombie 2.5x, NPCs 0.2x
|
||||||
|
|
||||||
|
### **VISUAL IMPROVEMENTS** ✅
|
||||||
|
- [x] Ultra white background removal (240+ brightness)
|
||||||
|
- [x] Off-white removal (cream, beige)
|
||||||
|
- [x] 21 new sprites added to transparency processing
|
||||||
|
- [x] Animals, NPCs, structures, objects
|
||||||
|
|
||||||
|
**Development Time:** 2.5 hours
|
||||||
|
**Code Written:** ~680 lines
|
||||||
|
**Systems Implemented:** 2 major + 4 subsystems
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## FAZA 0: Projektni Setup
|
## FAZA 0: Projektni Setup
|
||||||
**Status:** ✅ ODOBRENO (2025-12-06)
|
**Status:** ✅ ODOBRENO (2025-12-06)
|
||||||
|
|
||||||
|
|||||||
58
docs/phase22_plan.md
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
// PHASE 22: PLAYER CONTROLS INTEGRATION
|
||||||
|
// Implementation Plan
|
||||||
|
|
||||||
|
## 1. FARMING CONTROLS
|
||||||
|
### Priority: HIGH
|
||||||
|
- [ ] Detect hoe in hand (check inventory slot)
|
||||||
|
- [ ] Space/Click → till soil
|
||||||
|
- [ ] Detect seeds in hand
|
||||||
|
- [ ] Space/Click → plant seed
|
||||||
|
- [ ] Empty hand + ripe crop → harvest
|
||||||
|
- [ ] Animation feedback (swing tool)
|
||||||
|
- [ ] Sound effects (dig, plant, harvest)
|
||||||
|
- [ ] Particle effects (soil spray, seed drop)
|
||||||
|
|
||||||
|
## 2. RESOURCES DISPLAY
|
||||||
|
### Priority: HIGH
|
||||||
|
- [ ] Wood counter (top-right corner)
|
||||||
|
- [ ] Stone counter (below wood)
|
||||||
|
- [ ] Iron counter (below stone)
|
||||||
|
- [ ] Animated +X effect on gain
|
||||||
|
- [ ] Expandable panel
|
||||||
|
- [ ] Icon for each resource
|
||||||
|
|
||||||
|
## 3. DAY/NIGHT ENHANCEMENT
|
||||||
|
### Priority: MEDIUM
|
||||||
|
- [ ] Better time display (HH:MM format)
|
||||||
|
- [ ] Sky color transitions (blue → orange → dark)
|
||||||
|
- [ ] Dynamic lighting shader
|
||||||
|
- [ ] Speed control slider (1x/2x/5x)
|
||||||
|
- [ ] Pause button
|
||||||
|
|
||||||
|
## 4. BUILD MODE UI
|
||||||
|
### Priority: MEDIUM
|
||||||
|
- [ ] Tutorial popup on first B press
|
||||||
|
- [ ] Building name + cost display
|
||||||
|
- [ ] Building selection panel
|
||||||
|
- [ ] Rotate building (R key)
|
||||||
|
- [ ] Confirm placement (E key)
|
||||||
|
- [ ] Cancel (ESC key)
|
||||||
|
|
||||||
|
## 5. INVENTORY HOTBAR
|
||||||
|
### Priority: LOW
|
||||||
|
- [ ] Q/E keys for quick-swap
|
||||||
|
- [ ] Tool durability bar
|
||||||
|
- [ ] Seed count display
|
||||||
|
- [ ] Equipment preview
|
||||||
|
|
||||||
|
## 6. PLAYER FEEDBACK
|
||||||
|
### Priority: LOW
|
||||||
|
- [ ] Action cooldown timer
|
||||||
|
- [ ] Stamina system
|
||||||
|
- [ ] Tool swing animation
|
||||||
|
- [ ] Camera shake on harvest
|
||||||
|
- [ ] Screen flash effects
|
||||||
|
|
||||||
|
---
|
||||||
|
**START WITH:** Farming Controls + Resources Display
|
||||||
|
**ESTIMATED TIME:** 1-2 hours
|
||||||
@@ -82,6 +82,8 @@
|
|||||||
|
|
||||||
<!-- Systems -->
|
<!-- Systems -->
|
||||||
<script src="src/systems/TerrainSystem.js"></script>
|
<script src="src/systems/TerrainSystem.js"></script>
|
||||||
|
<script src="src/systems/FarmingSystem.js"></script>
|
||||||
|
<script src="src/systems/BuildSystem.js"></script>
|
||||||
<script src="src/systems/Antigravity.js"></script>
|
<script src="src/systems/Antigravity.js"></script>
|
||||||
<script src="src/systems/PathfindingSystem.js"></script>
|
<script src="src/systems/PathfindingSystem.js"></script>
|
||||||
<script src="src/systems/SaveSystem.js"></script>
|
<script src="src/systems/SaveSystem.js"></script>
|
||||||
@@ -92,7 +94,6 @@
|
|||||||
<script src="src/systems/LootSystem.js"></script>
|
<script src="src/systems/LootSystem.js"></script>
|
||||||
<script src="src/systems/InteractionSystem.js"></script>
|
<script src="src/systems/InteractionSystem.js"></script>
|
||||||
<script src="src/utils/InventoryIcons.js"></script> <!-- 2D Flat Icons -->
|
<script src="src/utils/InventoryIcons.js"></script> <!-- 2D Flat Icons -->
|
||||||
<script src="src/systems/FarmingSystem.js"></script>
|
|
||||||
<script src="src/systems/BuildingSystem.js"></script>
|
<script src="src/systems/BuildingSystem.js"></script>
|
||||||
<script src="src/systems/WeatherSystem.js"></script>
|
<script src="src/systems/WeatherSystem.js"></script>
|
||||||
<script src="src/systems/WorldEventSystem.js"></script>
|
<script src="src/systems/WorldEventSystem.js"></script>
|
||||||
|
|||||||
@@ -22,9 +22,10 @@
|
|||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"type": "commonjs",
|
"type": "commonjs",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"canvas": "^3.2.0",
|
||||||
"phaser": "^3.80.1"
|
"phaser": "^3.80.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"electron": "^33.2.1"
|
"electron": "^33.2.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -105,8 +105,11 @@ class NPC {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for animated sprites first
|
// Check for NEW isometric sprites first (zombie_worker)
|
||||||
if (this.type === 'zombie' && this.scene.textures.exists('zombie_walk')) {
|
if (this.type === 'zombie' && this.scene.textures.exists('zombie_worker')) {
|
||||||
|
texKey = 'zombie_worker'; // NEW: Isometric zombie sprite
|
||||||
|
isAnimated = false;
|
||||||
|
} else if (this.type === 'zombie' && this.scene.textures.exists('zombie_walk')) {
|
||||||
texKey = 'zombie_walk';
|
texKey = 'zombie_walk';
|
||||||
isAnimated = true;
|
isAnimated = true;
|
||||||
} else if (this.type === 'zombie' && this.scene.textures.exists('zombie_sprite')) {
|
} else if (this.type === 'zombie' && this.scene.textures.exists('zombie_sprite')) {
|
||||||
@@ -141,13 +144,16 @@ class NPC {
|
|||||||
this.sprite.setOrigin(0.5, 1);
|
this.sprite.setOrigin(0.5, 1);
|
||||||
|
|
||||||
if (isAnimated) {
|
if (isAnimated) {
|
||||||
this.sprite.setScale(1.5);
|
this.sprite.setScale(2.5); // MUCH larger (zombie worker)
|
||||||
} else {
|
} else {
|
||||||
// Scale po tipu
|
// Scale po tipu
|
||||||
let scale = 0.5; // Default
|
let scale = 0.3; // Default smaller
|
||||||
|
|
||||||
// New NPCs - smaller scale
|
// NEW: Isometric sprites (MUCH larger!)
|
||||||
if (this.type === 'cow') scale = 0.2;
|
if (texKey === 'zombie_worker') scale = 2.5;
|
||||||
|
else if (texKey === 'player_dreadlocks') scale = 2.5;
|
||||||
|
// New NPCs - SMALL scale
|
||||||
|
else if (this.type === 'cow') scale = 0.2;
|
||||||
else if (this.type === 'chicken') scale = 0.2;
|
else if (this.type === 'chicken') scale = 0.2;
|
||||||
else if (this.type === 'troll') scale = 0.2;
|
else if (this.type === 'troll') scale = 0.2;
|
||||||
else if (this.type === 'elf') scale = 0.2;
|
else if (this.type === 'elf') scale = 0.2;
|
||||||
@@ -188,9 +194,15 @@ class NPC {
|
|||||||
|
|
||||||
const targetScreen = this.iso.toScreen(targetX, targetY);
|
const targetScreen = this.iso.toScreen(targetX, targetY);
|
||||||
|
|
||||||
// Animation
|
// Animation - SAFE CHECK
|
||||||
if (this.sprite.texture.key === 'zombie_walk') {
|
if (this.sprite.texture.key === 'zombie_worker') {
|
||||||
this.sprite.play('zombie_walk_anim', true);
|
if (this.scene.anims.exists('zombie_worker_walk')) {
|
||||||
|
this.sprite.play('zombie_worker_walk', true);
|
||||||
|
}
|
||||||
|
} else if (this.sprite.texture.key === 'zombie_walk') {
|
||||||
|
if (this.scene.anims.exists('zombie_walk_anim')) {
|
||||||
|
this.sprite.play('zombie_walk_anim', true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tween za smooth gibanje
|
// Tween za smooth gibanje
|
||||||
@@ -205,7 +217,10 @@ class NPC {
|
|||||||
this.updatePosition();
|
this.updatePosition();
|
||||||
|
|
||||||
// Stop Animation
|
// Stop Animation
|
||||||
if (this.sprite.texture.key === 'zombie_walk') {
|
if (this.sprite.texture.key === 'zombie_worker') {
|
||||||
|
this.sprite.stop();
|
||||||
|
this.sprite.setFrame(0);
|
||||||
|
} else if (this.sprite.texture.key === 'zombie_walk') {
|
||||||
this.sprite.stop();
|
this.sprite.stop();
|
||||||
this.sprite.setFrame(0);
|
this.sprite.setFrame(0);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -86,14 +86,17 @@ class Player {
|
|||||||
}
|
}
|
||||||
|
|
||||||
createSprite() {
|
createSprite() {
|
||||||
// Prefer animated sprite if available
|
// NEW: Use player_dreadlocks sprite (isometric 2.5D)
|
||||||
let texKey = 'player_walk'; // Spritesheet
|
let texKey = 'player_dreadlocks';
|
||||||
let isAnimated = this.scene.textures.exists(texKey);
|
|
||||||
|
|
||||||
if (!isAnimated) {
|
// Fallback chain if new sprite not loaded
|
||||||
texKey = this.scene.textures.exists('player_sprite') ? 'player_sprite' : 'player';
|
if (!this.scene.textures.exists(texKey)) {
|
||||||
|
texKey = this.scene.textures.exists('player_walk') ? 'player_walk' : 'player_sprite';
|
||||||
if (!this.scene.textures.exists(texKey)) {
|
if (!this.scene.textures.exists(texKey)) {
|
||||||
TextureGenerator.createPlayerSprite(this.scene, texKey);
|
texKey = 'player';
|
||||||
|
if (!this.scene.textures.exists(texKey)) {
|
||||||
|
TextureGenerator.createPlayerSprite(this.scene, texKey);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -106,13 +109,16 @@ class Player {
|
|||||||
);
|
);
|
||||||
this.sprite.setOrigin(0.5, 1);
|
this.sprite.setOrigin(0.5, 1);
|
||||||
|
|
||||||
// Scale logic
|
// Scale based on sprite type
|
||||||
if (isAnimated) {
|
if (texKey === 'player_dreadlocks') {
|
||||||
this.sprite.setScale(1.5);
|
this.sprite.setScale(2.5); // MUCH larger for better visibility
|
||||||
|
} else if (texKey === 'player_walk') {
|
||||||
|
this.sprite.setScale(2.5); // Old animated sprite
|
||||||
} else {
|
} else {
|
||||||
this.sprite.setScale(0.5); // Povečano
|
this.sprite.setScale(1.2); // Old static sprite
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// --- HAND / HELD ITEM SPRITE ---
|
// --- HAND / HELD ITEM SPRITE ---
|
||||||
this.handSprite = this.scene.add.sprite(
|
this.handSprite = this.scene.add.sprite(
|
||||||
screenPos.x + this.offsetX + 10,
|
screenPos.x + this.offsetX + 10,
|
||||||
@@ -224,6 +230,11 @@ class Player {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.updateHeldItem();
|
this.updateHeldItem();
|
||||||
|
|
||||||
|
// SPACE KEY - Farming Action
|
||||||
|
if (this.keys.space && Phaser.Input.Keyboard.JustDown(this.keys.space)) {
|
||||||
|
this.handleFarmingAction();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
updateHeldItem() {
|
updateHeldItem() {
|
||||||
@@ -395,8 +406,15 @@ class Player {
|
|||||||
|
|
||||||
const targetScreen = this.iso.toScreen(targetX, targetY);
|
const targetScreen = this.iso.toScreen(targetX, targetY);
|
||||||
|
|
||||||
if (this.sprite.texture.key === 'player_walk') {
|
// Play walk animation - SAFE CHECK
|
||||||
this.sprite.play('player_walk_anim', true);
|
if (this.sprite.texture.key === 'player_dreadlocks') {
|
||||||
|
if (this.scene.anims.exists('player_dreadlocks_walk')) {
|
||||||
|
this.sprite.play('player_dreadlocks_walk', true);
|
||||||
|
}
|
||||||
|
} else if (this.sprite.texture.key === 'player_walk') {
|
||||||
|
if (this.scene.anims.exists('player_walk_anim')) {
|
||||||
|
this.sprite.play('player_walk_anim', true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.scene.tweens.add({
|
this.scene.tweens.add({
|
||||||
@@ -409,7 +427,11 @@ class Player {
|
|||||||
this.isMoving = false;
|
this.isMoving = false;
|
||||||
this.updatePosition();
|
this.updatePosition();
|
||||||
|
|
||||||
if (this.sprite.texture.key === 'player_walk') {
|
// Stop animation
|
||||||
|
if (this.sprite.texture.key === 'player_dreadlocks') {
|
||||||
|
this.sprite.stop();
|
||||||
|
this.sprite.setFrame(0);
|
||||||
|
} else if (this.sprite.texture.key === 'player_walk') {
|
||||||
this.sprite.stop();
|
this.sprite.stop();
|
||||||
this.sprite.setFrame(0);
|
this.sprite.setFrame(0);
|
||||||
}
|
}
|
||||||
@@ -479,4 +501,55 @@ class Player {
|
|||||||
this.sprite.clearTint();
|
this.sprite.clearTint();
|
||||||
this.sprite.angle = 0;
|
this.sprite.angle = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleFarmingAction() {
|
||||||
|
if (!this.scene.farmingSystem) return;
|
||||||
|
|
||||||
|
const uiScene = this.scene.scene.get('UIScene');
|
||||||
|
const invSys = this.scene.inventorySystem;
|
||||||
|
|
||||||
|
if (!uiScene || !invSys) return;
|
||||||
|
|
||||||
|
const selectedIdx = uiScene.selectedSlot;
|
||||||
|
const slot = invSys.slots[selectedIdx];
|
||||||
|
const itemType = slot ? slot.type : null;
|
||||||
|
|
||||||
|
// Get tile player is standing on
|
||||||
|
const gridX = this.gridX;
|
||||||
|
const gridY = this.gridY;
|
||||||
|
|
||||||
|
// HOE - Till soil
|
||||||
|
if (itemType === 'hoe') {
|
||||||
|
const success = this.scene.farmingSystem.tillSoil(gridX, gridY);
|
||||||
|
if (success) {
|
||||||
|
console.log('✅ Tilled soil!');
|
||||||
|
// TODO: Play dig sound
|
||||||
|
// TODO: Tool swing animation
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// SEEDS - Plant
|
||||||
|
if (itemType === 'carrot' || itemType === 'wheat' || itemType === 'item_seeds') {
|
||||||
|
const cropType = itemType === 'item_seeds' ? 'carrot' : itemType;
|
||||||
|
const success = this.scene.farmingSystem.plantSeed(gridX, gridY, cropType);
|
||||||
|
if (success) {
|
||||||
|
invSys.removeItem(itemType, 1);
|
||||||
|
console.log('🌱 Planted seed!');
|
||||||
|
// TODO: Play plant sound
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// EMPTY HAND - Harvest
|
||||||
|
if (!itemType) {
|
||||||
|
const success = this.scene.farmingSystem.harvestCrop(gridX, gridY);
|
||||||
|
if (success) {
|
||||||
|
console.log('🌾 Harvested crop!');
|
||||||
|
// TODO: Play harvest sound
|
||||||
|
// TODO: Screen shake
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,12 +41,31 @@ class GameScene extends Phaser.Scene {
|
|||||||
this.expansionSystem = new ExpansionSystem(this);
|
this.expansionSystem = new ExpansionSystem(this);
|
||||||
this.blueprintSystem = new BlueprintSystem(this);
|
this.blueprintSystem = new BlueprintSystem(this);
|
||||||
|
|
||||||
|
// Farm Stats Tracking for UI
|
||||||
|
this.farmStats = {
|
||||||
|
cropsPlanted: 0,
|
||||||
|
totalHarvested: 0,
|
||||||
|
goldEarned: 0,
|
||||||
|
daysFarmed: 0
|
||||||
|
};
|
||||||
|
|
||||||
|
// PARALLAX BACKGROUND - Clouds & Birds
|
||||||
|
this.createParallaxBackground();
|
||||||
|
|
||||||
// Inicializiraj terrain sistem - 100x100 mapa
|
// Inicializiraj terrain sistem - 100x100 mapa
|
||||||
console.log('🌍 Initializing terrain...');
|
console.log('🌍 Initializing terrain...');
|
||||||
try {
|
try {
|
||||||
this.terrainSystem = new TerrainSystem(this, 100, 100);
|
this.terrainSystem = new TerrainSystem(this, 100, 100);
|
||||||
this.terrainSystem.generate();
|
this.terrainSystem.generate();
|
||||||
|
|
||||||
|
// Initialize Farming System
|
||||||
|
this.farmingSystem = new FarmingSystem(this);
|
||||||
|
console.log('🌾 Farming system initialized!');
|
||||||
|
|
||||||
|
// Initialize Build System
|
||||||
|
this.buildSystem = new BuildSystem(this);
|
||||||
|
console.log('🏗️ Build system initialized!');
|
||||||
|
|
||||||
// Terrain offset
|
// Terrain offset
|
||||||
this.terrainOffsetX = width / 2;
|
this.terrainOffsetX = width / 2;
|
||||||
this.terrainOffsetY = 100;
|
this.terrainOffsetY = 100;
|
||||||
@@ -176,6 +195,24 @@ class GameScene extends Phaser.Scene {
|
|||||||
console.log('🧟⚒️ Initializing Zombie Worker System...');
|
console.log('🧟⚒️ Initializing Zombie Worker System...');
|
||||||
this.zombieWorkerSystem = new ZombieWorkerSystem(this);
|
this.zombieWorkerSystem = new ZombieWorkerSystem(this);
|
||||||
|
|
||||||
|
// SPAWN STARTER ZOMBIE WORKER (8x8 Farm)
|
||||||
|
console.log('🧟 Spawning STARTER Zombie Worker...');
|
||||||
|
const starterZombieX = 48; // Inside 8x8 farm (center is 50,50, farm is 46-54)
|
||||||
|
const starterZombieY = 48;
|
||||||
|
const starterZombie = new NPC(this, starterZombieX, starterZombieY, this.terrainOffsetX, this.terrainOffsetY, 'zombie');
|
||||||
|
// Auto-tame the starter zombie
|
||||||
|
starterZombie.isTamed = true; // Use isTamed (not just tamed)
|
||||||
|
starterZombie.state = 'IDLE';
|
||||||
|
if (starterZombie.showEmote) {
|
||||||
|
starterZombie.showEmote('👋'); // Friendly wave
|
||||||
|
}
|
||||||
|
this.npcs.push(starterZombie);
|
||||||
|
// Assign to farming work
|
||||||
|
if (this.zombieWorkerSystem) {
|
||||||
|
this.zombieWorkerSystem.assignWork(starterZombie, 'FARM', 5); // Farming work, 5 tile radius
|
||||||
|
}
|
||||||
|
console.log('✅ Starter zombie worker spawned and assigned to farming!');
|
||||||
|
|
||||||
// GRAVE SYSTEM
|
// GRAVE SYSTEM
|
||||||
console.log('🪦 Initializing Grave System...');
|
console.log('🪦 Initializing Grave System...');
|
||||||
this.graveSystem = new GraveSystem(this);
|
this.graveSystem = new GraveSystem(this);
|
||||||
@@ -495,24 +532,34 @@ class GameScene extends Phaser.Scene {
|
|||||||
|
|
||||||
// Build Mode Keys
|
// Build Mode Keys
|
||||||
this.input.keyboard.on('keydown-B', () => {
|
this.input.keyboard.on('keydown-B', () => {
|
||||||
if (this.buildingSystem) this.buildingSystem.toggleBuildMode();
|
if (this.buildSystem) this.buildSystem.toggleBuildMode();
|
||||||
});
|
});
|
||||||
|
|
||||||
this.input.keyboard.on('keydown-ONE', () => {
|
this.input.keyboard.on('keydown-ONE', () => {
|
||||||
if (this.buildingSystem && this.buildingSystem.isBuildMode) this.buildingSystem.selectBuilding('fence');
|
if (this.buildSystem && this.buildSystem.buildMode) this.buildSystem.selectBuilding('fence_post');
|
||||||
else if (this.scene.get('UIScene')) this.scene.get('UIScene').selectSlot(0);
|
else if (this.scene.get('UIScene')) this.scene.get('UIScene').selectSlot(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.input.keyboard.on('keydown-TWO', () => {
|
this.input.keyboard.on('keydown-TWO', () => {
|
||||||
if (this.buildingSystem && this.buildingSystem.isBuildMode) this.buildingSystem.selectBuilding('wall');
|
if (this.buildSystem && this.buildSystem.buildMode) this.buildSystem.selectBuilding('fence_horizontal');
|
||||||
else if (this.scene.get('UIScene')) this.scene.get('UIScene').selectSlot(1);
|
else if (this.scene.get('UIScene')) this.scene.get('UIScene').selectSlot(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.input.keyboard.on('keydown-THREE', () => {
|
this.input.keyboard.on('keydown-THREE', () => {
|
||||||
if (this.buildingSystem && this.buildingSystem.isBuildMode) this.buildingSystem.selectBuilding('house');
|
if (this.buildSystem && this.buildSystem.buildMode) this.buildSystem.selectBuilding('fence_vertical');
|
||||||
else if (this.scene.get('UIScene')) this.scene.get('UIScene').selectSlot(2);
|
else if (this.scene.get('UIScene')) this.scene.get('UIScene').selectSlot(2);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.input.keyboard.on('keydown-FOUR', () => {
|
||||||
|
if (this.buildSystem && this.buildSystem.buildMode) this.buildSystem.selectBuilding('fence_corner');
|
||||||
|
else if (this.scene.get('UIScene')) this.scene.get('UIScene').selectSlot(3);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.input.keyboard.on('keydown-FIVE', () => {
|
||||||
|
if (this.buildSystem && this.buildSystem.buildMode) this.buildSystem.selectBuilding('barn');
|
||||||
|
else if (this.scene.get('UIScene')) this.scene.get('UIScene').selectSlot(4);
|
||||||
|
});
|
||||||
|
|
||||||
// Soft Reset (F4)
|
// Soft Reset (F4)
|
||||||
this.input.keyboard.on('keydown-F4', () => window.location.reload());
|
this.input.keyboard.on('keydown-F4', () => window.location.reload());
|
||||||
|
|
||||||
@@ -530,6 +577,7 @@ class GameScene extends Phaser.Scene {
|
|||||||
if (this.lootSystem) this.lootSystem.update(delta);
|
if (this.lootSystem) this.lootSystem.update(delta);
|
||||||
if (this.interactionSystem) this.interactionSystem.update(delta);
|
if (this.interactionSystem) this.interactionSystem.update(delta);
|
||||||
if (this.farmingSystem) this.farmingSystem.update(delta);
|
if (this.farmingSystem) this.farmingSystem.update(delta);
|
||||||
|
if (this.buildSystem) this.buildSystem.update(delta);
|
||||||
if (this.questSystem) this.questSystem.update(delta);
|
if (this.questSystem) this.questSystem.update(delta);
|
||||||
if (this.multiplayerSystem) this.multiplayerSystem.update(delta);
|
if (this.multiplayerSystem) this.multiplayerSystem.update(delta);
|
||||||
|
|
||||||
@@ -816,6 +864,9 @@ class GameScene extends Phaser.Scene {
|
|||||||
window.Antigravity.Update(this, delta);
|
window.Antigravity.Update(this, delta);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Parallax background update
|
||||||
|
this.updateParallax(delta);
|
||||||
|
|
||||||
// Terrain system update (za water animacijo)
|
// Terrain system update (za water animacijo)
|
||||||
if (this.terrainSystem && this.terrainSystem.update) {
|
if (this.terrainSystem && this.terrainSystem.update) {
|
||||||
this.terrainSystem.update(Date.now(), delta);
|
this.terrainSystem.update(Date.now(), delta);
|
||||||
@@ -823,4 +874,69 @@ class GameScene extends Phaser.Scene {
|
|||||||
console.warn('⚠️ TerrainSystem.update not available!');
|
console.warn('⚠️ TerrainSystem.update not available!');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
createParallaxBackground() {
|
||||||
|
const width = this.cameras.main.width;
|
||||||
|
const height = this.cameras.main.height;
|
||||||
|
|
||||||
|
// Parallax container
|
||||||
|
this.parallaxContainer = this.add.container(0, 0);
|
||||||
|
this.parallaxContainer.setDepth(-1000); // Always behind everything
|
||||||
|
|
||||||
|
this.clouds = [];
|
||||||
|
this.birds = [];
|
||||||
|
|
||||||
|
// Create 5 clouds
|
||||||
|
for (let i = 0; i < 5; i++) {
|
||||||
|
const cloud = this.add.text(
|
||||||
|
Math.random() * width * 2,
|
||||||
|
Math.random() * height * 0.5,
|
||||||
|
'☁️',
|
||||||
|
{ fontSize: `${30 + Math.random() * 20}px` }
|
||||||
|
);
|
||||||
|
cloud.speed = 0.3 + Math.random() * 0.2; // 0.3-0.5x speed
|
||||||
|
this.parallaxContainer.add(cloud);
|
||||||
|
this.clouds.push(cloud);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create 3 birds
|
||||||
|
for (let i = 0; i < 3; i++) {
|
||||||
|
const bird = this.add.text(
|
||||||
|
Math.random() * width * 2,
|
||||||
|
100 + Math.random() * 200,
|
||||||
|
'🐦',
|
||||||
|
{ fontSize: '20px' }
|
||||||
|
);
|
||||||
|
bird.speed = 0.5 + Math.random() * 0.3; // 0.5-0.8x speed
|
||||||
|
this.parallaxContainer.add(bird);
|
||||||
|
this.birds.push(bird);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('☁️ Parallax background created!');
|
||||||
|
}
|
||||||
|
|
||||||
|
updateParallax(delta) {
|
||||||
|
if (!this.parallaxContainer) return;
|
||||||
|
|
||||||
|
const width = this.cameras.main.width;
|
||||||
|
|
||||||
|
// Update clouds
|
||||||
|
this.clouds.forEach(cloud => {
|
||||||
|
cloud.x -= cloud.speed * (delta / 16);
|
||||||
|
if (cloud.x < -100) {
|
||||||
|
cloud.x = width + 100;
|
||||||
|
cloud.y = Math.random() * this.cameras.main.height * 0.5;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Update birds
|
||||||
|
this.birds.forEach(bird => {
|
||||||
|
bird.x -= bird.speed * (delta / 16);
|
||||||
|
bird.y += Math.sin(Date.now() / 1000 + bird.x) * 0.5; // Flutter effect
|
||||||
|
if (bird.x < -100) {
|
||||||
|
bird.x = width + 100;
|
||||||
|
bird.y = 100 + Math.random() * 200;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -78,12 +78,44 @@ class PreloadScene extends Phaser.Scene {
|
|||||||
this.load.image('fence_full', 'assets/fence_full.png');
|
this.load.image('fence_full', 'assets/fence_full.png');
|
||||||
this.load.image('wall_damaged', 'assets/wall_damaged.png');
|
this.load.image('wall_damaged', 'assets/wall_damaged.png');
|
||||||
|
|
||||||
|
|
||||||
// Voxel stil asset-i (2.5D)
|
// Voxel stil asset-i (2.5D)
|
||||||
this.load.image('tree_voxel_green', 'assets/tree_voxel_green.png');
|
this.load.image('tree_voxel_green', 'assets/tree_voxel_green.png');
|
||||||
this.load.image('tree_voxel_blue', 'assets/tree_voxel_blue.png');
|
this.load.image('tree_voxel_blue', 'assets/tree_voxel_blue.png');
|
||||||
this.load.image('tree_voxel_dead', 'assets/tree_voxel_dead.png');
|
this.load.image('tree_voxel_dead', 'assets/tree_voxel_dead.png');
|
||||||
this.load.image('rock_voxel', 'assets/rock_voxel.png');
|
this.load.image('rock_voxel', 'assets/rock_voxel.png');
|
||||||
|
|
||||||
|
// NEW ISOMETRIC 2.5D ASSETS (Stardew Valley style)
|
||||||
|
// Buildings & Structures
|
||||||
|
this.load.image('barn_isometric', 'assets/barn_isometric.png');
|
||||||
|
this.load.image('farmhouse_isometric', 'assets/farmhouse_isometric.png');
|
||||||
|
this.load.image('fence_isometric', 'assets/fence_isometric.png');
|
||||||
|
this.load.image('bridge_isometric', 'assets/bridge_isometric.png');
|
||||||
|
this.load.image('blacksmith_workshop', 'assets/blacksmith_workshop.png');
|
||||||
|
this.load.image('ruins_building', 'assets/ruins_building.png');
|
||||||
|
|
||||||
|
// Farm & Crops
|
||||||
|
this.load.image('soil_tilled', 'assets/soil_tilled.png');
|
||||||
|
this.load.image('carrots_stages', 'assets/carrots_stages.png');
|
||||||
|
this.load.image('flowers_pink_isometric', 'assets/flowers_pink_isometric.png');
|
||||||
|
|
||||||
|
// Characters - SPRITESHEETS (6 frames @ 64x64 each)
|
||||||
|
this.load.spritesheet('player_dreadlocks', 'assets/player_dreadlocks.png', {
|
||||||
|
frameWidth: 64,
|
||||||
|
frameHeight: 64
|
||||||
|
});
|
||||||
|
this.load.spritesheet('zombie_worker', 'assets/zombie_worker.png', {
|
||||||
|
frameWidth: 64,
|
||||||
|
frameHeight: 64
|
||||||
|
});
|
||||||
|
this.load.image('grave_zombie', 'assets/grave_zombie.png');
|
||||||
|
|
||||||
|
// Fence pieces (separate parts)
|
||||||
|
this.load.image('fence_post', 'assets/fence_post.png');
|
||||||
|
this.load.image('fence_horizontal', 'assets/fence_horizontal.png');
|
||||||
|
this.load.image('fence_vertical', 'assets/fence_vertical.png');
|
||||||
|
this.load.image('fence_corner', 'assets/fence_corner.png');
|
||||||
|
|
||||||
// Wait for load completion then process transparency
|
// Wait for load completion then process transparency
|
||||||
this.load.once('complete', () => {
|
this.load.once('complete', () => {
|
||||||
this.processAllTransparency();
|
this.processAllTransparency();
|
||||||
@@ -98,6 +130,7 @@ class PreloadScene extends Phaser.Scene {
|
|||||||
createAnimations() {
|
createAnimations() {
|
||||||
if (this.anims.exists('player_walk_anim')) return;
|
if (this.anims.exists('player_walk_anim')) return;
|
||||||
|
|
||||||
|
// Old animations
|
||||||
this.anims.create({
|
this.anims.create({
|
||||||
key: 'player_walk_anim',
|
key: 'player_walk_anim',
|
||||||
frames: this.anims.generateFrameNumbers('player_walk', { start: 0, end: 5 }),
|
frames: this.anims.generateFrameNumbers('player_walk', { start: 0, end: 5 }),
|
||||||
@@ -111,6 +144,22 @@ class PreloadScene extends Phaser.Scene {
|
|||||||
frameRate: 8,
|
frameRate: 8,
|
||||||
repeat: -1
|
repeat: -1
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// NEW: Isometric character animations (6 frames)
|
||||||
|
this.anims.create({
|
||||||
|
key: 'player_dreadlocks_walk',
|
||||||
|
frames: this.anims.generateFrameNumbers('player_dreadlocks', { start: 0, end: 5 }),
|
||||||
|
frameRate: 10,
|
||||||
|
repeat: -1
|
||||||
|
});
|
||||||
|
|
||||||
|
this.anims.create({
|
||||||
|
key: 'zombie_worker_walk',
|
||||||
|
frames: this.anims.generateFrameNumbers('zombie_worker', { start: 0, end: 5 }),
|
||||||
|
frameRate: 8,
|
||||||
|
repeat: -1
|
||||||
|
});
|
||||||
|
|
||||||
console.log('🎞️ Animations created!');
|
console.log('🎞️ Animations created!');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -162,13 +211,58 @@ class PreloadScene extends Phaser.Scene {
|
|||||||
'tree_voxel_green',
|
'tree_voxel_green',
|
||||||
'tree_voxel_blue',
|
'tree_voxel_blue',
|
||||||
'tree_voxel_dead',
|
'tree_voxel_dead',
|
||||||
'rock_voxel'
|
'rock_voxel',
|
||||||
|
|
||||||
|
// NEW ISOMETRIC 2.5D ASSETS - Remove transparency
|
||||||
|
'barn_isometric',
|
||||||
|
'farmhouse_isometric',
|
||||||
|
'fence_isometric',
|
||||||
|
'bridge_isometric',
|
||||||
|
'blacksmith_workshop',
|
||||||
|
'ruins_building',
|
||||||
|
'soil_tilled',
|
||||||
|
'carrots_stages',
|
||||||
|
'flowers_pink_isometric',
|
||||||
|
// NOTE: player_dreadlocks and zombie_worker are SPRITESHEETS - don't process!
|
||||||
|
'grave_zombie',
|
||||||
|
|
||||||
|
// NEW FENCE PIECES
|
||||||
|
'fence_post',
|
||||||
|
'fence_horizontal',
|
||||||
|
'fence_vertical',
|
||||||
|
'fence_corner',
|
||||||
|
|
||||||
|
// ANIMALS & NPCs
|
||||||
|
'chicken',
|
||||||
|
'cow',
|
||||||
|
'cow_mutant',
|
||||||
|
'elf',
|
||||||
|
'troll',
|
||||||
|
'villager',
|
||||||
|
'npc_merchant',
|
||||||
|
'npc_zombie',
|
||||||
|
|
||||||
|
// STRUCTURES
|
||||||
|
'structure_house',
|
||||||
|
'wall_damaged',
|
||||||
|
'city_wall',
|
||||||
|
|
||||||
|
// MISC OBJECTS
|
||||||
|
'wheat_sprite',
|
||||||
|
'grass_sprite',
|
||||||
|
'leaf_sprite',
|
||||||
|
'stone_sprite'
|
||||||
];
|
];
|
||||||
|
|
||||||
spritesToProcess.forEach(spriteKey => {
|
spritesToProcess.forEach(spriteKey => {
|
||||||
this.processSpriteTransparency(spriteKey);
|
this.processSpriteTransparency(spriteKey);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// ULTRA AGGRESSIVE: Fence Post only
|
||||||
|
if (this.textures.exists('fence_post')) {
|
||||||
|
this.ultraRemoveBackground('fence_post');
|
||||||
|
}
|
||||||
|
|
||||||
console.log('✅ All sprites transparency processed!');
|
console.log('✅ All sprites transparency processed!');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -191,21 +285,45 @@ class PreloadScene extends Phaser.Scene {
|
|||||||
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
|
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
|
||||||
const data = imageData.data;
|
const data = imageData.data;
|
||||||
|
|
||||||
// Remove backgrounds
|
// Remove backgrounds - IMPROVED CHECKBOARD DETECTION
|
||||||
for (let i = 0; i < data.length; i += 4) {
|
for (let i = 0; i < data.length; i += 4) {
|
||||||
const r = data[i];
|
const r = data[i];
|
||||||
const g = data[i + 1];
|
const g = data[i + 1];
|
||||||
const b = data[i + 2];
|
const b = data[i + 2];
|
||||||
|
|
||||||
// Remove white/light gray backgrounds (Checkerboard & White)
|
// Remove ALL grayscale colors (checkboard pattern)
|
||||||
// Target grays: R,G,B should be similar and high value.
|
// Checkboard has both light (204,204,204) and dark (153,153,153) squares
|
||||||
if (r > 150 && g > 150 && b > 150) {
|
const isGrayscale = Math.abs(r - g) < 15 && Math.abs(g - b) < 15 && Math.abs(r - b) < 15;
|
||||||
// Check if it's grayscale (checkerboard is usually perfect gray)
|
|
||||||
if (Math.abs(r - g) < 30 && Math.abs(g - b) < 30) {
|
if (isGrayscale) {
|
||||||
data[i + 3] = 0;
|
// ULTRA-AGGRESSIVE: Remove ALL grays from 100-240
|
||||||
|
const isAnyGray = r >= 100 && r <= 240;
|
||||||
|
|
||||||
|
if (isAnyGray) {
|
||||||
|
data[i + 3] = 0; // Make transparent
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AGGRESSIVE: Remove ALL light backgrounds (AI-generated sprites)
|
||||||
|
const brightness = (r + g + b) / 3;
|
||||||
|
const isVeryLight = brightness > 170 && Math.abs(r - g) < 50 && Math.abs(g - b) < 50;
|
||||||
|
|
||||||
|
if (isVeryLight) {
|
||||||
|
data[i + 3] = 0; // Make transparent
|
||||||
|
}
|
||||||
|
|
||||||
|
// ULTRA AGGRESSIVE: Remove PURE WHITE backgrounds
|
||||||
|
const isWhite = r > 240 && g > 240 && b > 240;
|
||||||
|
if (isWhite) {
|
||||||
|
data[i + 3] = 0; // Make transparent
|
||||||
|
}
|
||||||
|
|
||||||
|
// ULTRA AGGRESSIVE: Remove OFF-WHITE backgrounds (cream, beige)
|
||||||
|
const isOffWhite = brightness > 230 && Math.abs(r - g) < 20 && Math.abs(g - b) < 20;
|
||||||
|
if (isOffWhite) {
|
||||||
|
data[i + 3] = 0; // Make transparent
|
||||||
|
}
|
||||||
|
|
||||||
// Special: Remove brown/tan backgrounds (merchant sprite)
|
// Special: Remove brown/tan backgrounds (merchant sprite)
|
||||||
if (spriteKey === 'merchant_sprite') {
|
if (spriteKey === 'merchant_sprite') {
|
||||||
// Brown detection: R > G > B, warm tones
|
// Brown detection: R > G > B, warm tones
|
||||||
@@ -360,4 +478,42 @@ class PreloadScene extends Phaser.Scene {
|
|||||||
startGame();
|
startGame();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ultraRemoveBackground(spriteKey) {
|
||||||
|
if (!this.textures.exists(spriteKey)) return;
|
||||||
|
|
||||||
|
const texture = this.textures.get(spriteKey);
|
||||||
|
const source = texture.getSourceImage();
|
||||||
|
|
||||||
|
const canvas = document.createElement('canvas');
|
||||||
|
canvas.width = source.width;
|
||||||
|
canvas.height = source.height;
|
||||||
|
const ctx = canvas.getContext('2d', { willReadFrequently: true });
|
||||||
|
|
||||||
|
ctx.drawImage(source, 0, 0);
|
||||||
|
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
|
||||||
|
const data = imageData.data;
|
||||||
|
|
||||||
|
// Remove EVERYTHING except brown wood colors
|
||||||
|
for (let i = 0; i < data.length; i += 4) {
|
||||||
|
const r = data[i];
|
||||||
|
const g = data[i + 1];
|
||||||
|
const b = data[i + 2];
|
||||||
|
|
||||||
|
// Keep only brown/wood colors: R > G > B and R is dominant
|
||||||
|
const isBrown = r > g && g > b && r > 80 && r < 200;
|
||||||
|
|
||||||
|
if (!isBrown) {
|
||||||
|
data[i + 3] = 0; // Make transparent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.putImageData(imageData, 0, 0);
|
||||||
|
|
||||||
|
// Remove old texture and create new one from canvas
|
||||||
|
this.textures.remove(spriteKey);
|
||||||
|
this.textures.addCanvas(spriteKey, canvas);
|
||||||
|
|
||||||
|
console.log(`🔥 ULTRA removed background from ${spriteKey}`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,17 +11,60 @@ class StoryScene extends Phaser.Scene {
|
|||||||
const bg = this.add.rectangle(0, 0, width, height, 0x0a0a0a);
|
const bg = this.add.rectangle(0, 0, width, height, 0x0a0a0a);
|
||||||
bg.setOrigin(0);
|
bg.setOrigin(0);
|
||||||
|
|
||||||
// Title
|
// GLOW EFFECT for title (multiple Text objects)
|
||||||
|
const titleGlow1 = this.add.text(width / 2, 80, 'NOVAFARMA', {
|
||||||
|
fontSize: '72px',
|
||||||
|
fontFamily: 'Courier New',
|
||||||
|
color: '#00ff41',
|
||||||
|
fontStyle: 'bold'
|
||||||
|
});
|
||||||
|
titleGlow1.setOrigin(0.5);
|
||||||
|
titleGlow1.setAlpha(0.3);
|
||||||
|
titleGlow1.setScale(1.05);
|
||||||
|
|
||||||
|
const titleGlow2 = this.add.text(width / 2, 80, 'NOVAFARMA', {
|
||||||
|
fontSize: '72px',
|
||||||
|
fontFamily: 'Courier New',
|
||||||
|
color: '#00ff41',
|
||||||
|
fontStyle: 'bold'
|
||||||
|
});
|
||||||
|
titleGlow2.setOrigin(0.5);
|
||||||
|
titleGlow2.setAlpha(0.5);
|
||||||
|
titleGlow2.setScale(1.02);
|
||||||
|
|
||||||
|
// Main title
|
||||||
const title = this.add.text(width / 2, 80, 'NOVAFARMA', {
|
const title = this.add.text(width / 2, 80, 'NOVAFARMA', {
|
||||||
fontSize: '72px',
|
fontSize: '72px',
|
||||||
fontFamily: 'Courier New',
|
fontFamily: 'Courier New',
|
||||||
color: '#00ff41',
|
color: '#00ff41',
|
||||||
fontStyle: 'bold',
|
fontStyle: 'bold',
|
||||||
stroke: '#000000',
|
stroke: '#003311',
|
||||||
strokeThickness: 6
|
strokeThickness: 8
|
||||||
});
|
});
|
||||||
title.setOrigin(0.5);
|
title.setOrigin(0.5);
|
||||||
|
|
||||||
|
// Pulsing glow animation
|
||||||
|
this.tweens.add({
|
||||||
|
targets: [titleGlow1, titleGlow2],
|
||||||
|
scaleX: 1.08,
|
||||||
|
scaleY: 1.08,
|
||||||
|
alpha: 0.6,
|
||||||
|
duration: 2000,
|
||||||
|
yoyo: true,
|
||||||
|
repeat: -1,
|
||||||
|
ease: 'Sine.easeInOut'
|
||||||
|
});
|
||||||
|
|
||||||
|
// Subtle title bounce
|
||||||
|
this.tweens.add({
|
||||||
|
targets: title,
|
||||||
|
y: 75,
|
||||||
|
duration: 3000,
|
||||||
|
yoyo: true,
|
||||||
|
repeat: -1,
|
||||||
|
ease: 'Sine.easeInOut'
|
||||||
|
});
|
||||||
|
|
||||||
// Subtitle
|
// Subtitle
|
||||||
const subtitle = this.add.text(width / 2, 150, '2084 - Survival Farm', {
|
const subtitle = this.add.text(width / 2, 150, '2084 - Survival Farm', {
|
||||||
fontSize: '20px',
|
fontSize: '20px',
|
||||||
|
|||||||
@@ -91,6 +91,8 @@ class UIScene extends Phaser.Scene {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.createOxygenBar();
|
this.createOxygenBar();
|
||||||
|
this.createZombieStatsPanel();
|
||||||
|
this.createFarmStatsPanel();
|
||||||
}
|
}
|
||||||
|
|
||||||
// ... (rest of class) ...
|
// ... (rest of class) ...
|
||||||
@@ -318,6 +320,8 @@ class UIScene extends Phaser.Scene {
|
|||||||
// Re-create UI elements at new positions
|
// Re-create UI elements at new positions
|
||||||
this.createClock();
|
this.createClock();
|
||||||
this.createGoldDisplay();
|
this.createGoldDisplay();
|
||||||
|
this.createResourcesDisplay();
|
||||||
|
this.createTimeControlPanel();
|
||||||
this.createInventoryBar();
|
this.createInventoryBar();
|
||||||
this.createInventoryBar();
|
this.createInventoryBar();
|
||||||
this.createDebugInfo();
|
this.createDebugInfo();
|
||||||
@@ -580,6 +584,62 @@ class UIScene extends Phaser.Scene {
|
|||||||
this.clockText.setOrigin(0.5, 0.5);
|
this.clockText.setOrigin(0.5, 0.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
createResourcesDisplay() {
|
||||||
|
const startX = this.scale.width - 160;
|
||||||
|
const startY = 110; // Below gold display
|
||||||
|
const spacing = 35;
|
||||||
|
|
||||||
|
// Container
|
||||||
|
this.resourcesContainer = this.add.container(0, 0);
|
||||||
|
this.resourcesContainer.setDepth(1000);
|
||||||
|
|
||||||
|
// Resources to display
|
||||||
|
const resources = [
|
||||||
|
{ key: 'wood', label: '🪵', color: '#8B4513' },
|
||||||
|
{ key: 'stone', label: '🪨', color: '#808080' },
|
||||||
|
{ key: 'iron', label: '⚙️', color: '#C0C0C0' }
|
||||||
|
];
|
||||||
|
|
||||||
|
this.resourceTexts = {};
|
||||||
|
|
||||||
|
resources.forEach((res, i) => {
|
||||||
|
const y = startY + (i * spacing);
|
||||||
|
|
||||||
|
// Background
|
||||||
|
const bg = this.add.graphics();
|
||||||
|
bg.fillStyle(0x1a1a1a, 0.7);
|
||||||
|
bg.fillRect(startX, y, 140, 28);
|
||||||
|
bg.lineStyle(2, parseInt(res.color.replace('#', '0x')), 0.6);
|
||||||
|
bg.strokeRect(startX, y, 140, 28);
|
||||||
|
this.resourcesContainer.add(bg);
|
||||||
|
|
||||||
|
// Icon/Label
|
||||||
|
const icon = this.add.text(startX + 10, y + 14, res.label, {
|
||||||
|
fontSize: '18px'
|
||||||
|
}).setOrigin(0, 0.5);
|
||||||
|
this.resourcesContainer.add(icon);
|
||||||
|
|
||||||
|
// Count text
|
||||||
|
const text = this.add.text(startX + 40, y + 14, `${res.key.toUpperCase()}: 0`, {
|
||||||
|
fontSize: '14px',
|
||||||
|
fontFamily: 'Courier New',
|
||||||
|
fill: res.color,
|
||||||
|
fontStyle: 'bold'
|
||||||
|
}).setOrigin(0, 0.5);
|
||||||
|
this.resourcesContainer.add(text);
|
||||||
|
|
||||||
|
this.resourceTexts[res.key] = text;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
updateResourceDisplay(resource, amount) {
|
||||||
|
if (!this.resourceTexts) return;
|
||||||
|
const text = this.resourceTexts[resource];
|
||||||
|
if (text) {
|
||||||
|
text.setText(`${resource.toUpperCase()}: ${amount}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
createGoldDisplay() {
|
createGoldDisplay() {
|
||||||
if (this.goldBg) this.goldBg.destroy();
|
if (this.goldBg) this.goldBg.destroy();
|
||||||
if (this.goldText) this.goldText.destroy();
|
if (this.goldText) this.goldText.destroy();
|
||||||
@@ -660,7 +720,141 @@ class UIScene extends Phaser.Scene {
|
|||||||
this.setBarValue(this.hungerBar, stats.hunger);
|
this.setBarValue(this.hungerBar, stats.hunger);
|
||||||
this.setBarValue(this.thirstBar, stats.thirst);
|
this.setBarValue(this.thirstBar, stats.thirst);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update Zombie Stats Panel (starter zombie worker)
|
||||||
|
if (this.gameScene.npcs && this.gameScene.npcs.length > 0) {
|
||||||
|
const zombieWorker = this.gameScene.npcs.find(npc => npc.type === 'zombie' && npc.isTamed);
|
||||||
|
if (zombieWorker) {
|
||||||
|
this.updateZombieStats(zombieWorker);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update Farm Stats Panel
|
||||||
|
if (this.gameScene.farmStats) {
|
||||||
|
this.updateFarmStats(this.gameScene.farmStats);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update Clock Display (HH:MM format)
|
||||||
|
if (this.clockText && this.gameScene.timeSystem) {
|
||||||
|
const ts = this.gameScene.timeSystem;
|
||||||
|
const hour = Math.floor(ts.hour);
|
||||||
|
const minute = Math.floor((ts.hour - hour) * 60);
|
||||||
|
const hourStr = hour.toString().padStart(2, '0');
|
||||||
|
const minStr = minute.toString().padStart(2, '0');
|
||||||
|
|
||||||
|
// Day/Night indicator
|
||||||
|
const isNight = hour < 6 || hour >= 18;
|
||||||
|
const period = isNight ? '🌙' : '☀️';
|
||||||
|
|
||||||
|
this.clockText.setText(`Day ${ts.day} - ${hourStr}:${minStr} ${period}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update Resources Display
|
||||||
|
if (this.resourceTexts && this.gameScene.inventorySystem) {
|
||||||
|
const inv = this.gameScene.inventorySystem;
|
||||||
|
this.updateResourceDisplay('wood', inv.getItemCount('wood'));
|
||||||
|
this.updateResourceDisplay('stone', inv.getItemCount('stone'));
|
||||||
|
this.updateResourceDisplay('iron', inv.getItemCount('iron'));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
createTimeControlPanel() {
|
||||||
|
const x = this.scale.width - 170;
|
||||||
|
const y = 250; // Below resources
|
||||||
|
|
||||||
|
this.timeControlContainer = this.add.container(x, y);
|
||||||
|
this.timeControlContainer.setDepth(1000);
|
||||||
|
|
||||||
|
// Background
|
||||||
|
const bg = this.add.graphics();
|
||||||
|
bg.fillStyle(0x1a1a2a, 0.8);
|
||||||
|
bg.fillRect(0, 0, 150, 100);
|
||||||
|
bg.lineStyle(2, 0x4a90e2, 0.8);
|
||||||
|
bg.strokeRect(0, 0, 150, 100);
|
||||||
|
this.timeControlContainer.add(bg);
|
||||||
|
|
||||||
|
// Title
|
||||||
|
const title = this.add.text(75, 15, 'TIME SPEED', {
|
||||||
|
fontSize: '12px',
|
||||||
|
fontFamily: 'Courier New',
|
||||||
|
fill: '#ffffff',
|
||||||
|
fontStyle: 'bold'
|
||||||
|
}).setOrigin(0.5, 0.5);
|
||||||
|
this.timeControlContainer.add(title);
|
||||||
|
|
||||||
|
// Speed buttons
|
||||||
|
const speeds = [
|
||||||
|
{ label: '1x', value: 1.0, y: 40 },
|
||||||
|
{ label: '2x', value: 2.0, y: 60 },
|
||||||
|
{ label: '5x', value: 5.0, y: 80 }
|
||||||
|
];
|
||||||
|
|
||||||
|
this.timeSpeedButtons = [];
|
||||||
|
|
||||||
|
speeds.forEach((speed, i) => {
|
||||||
|
const btn = this.add.text(75, speed.y, speed.label, {
|
||||||
|
fontSize: '14px',
|
||||||
|
fontFamily: 'Courier New',
|
||||||
|
fill: '#4a90e2',
|
||||||
|
fontStyle: 'bold',
|
||||||
|
backgroundColor: '#1a1a2a',
|
||||||
|
padding: { x: 20, y: 5 }
|
||||||
|
}).setOrigin(0.5, 0.5);
|
||||||
|
|
||||||
|
btn.setInteractive({ useHandCursor: true });
|
||||||
|
btn.on('pointerdown', () => {
|
||||||
|
this.setTimeSpeed(speed.value);
|
||||||
|
this.highlightSpeedButton(i);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.timeControlContainer.add(btn);
|
||||||
|
this.timeSpeedButtons.push(btn);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.highlightSpeedButton(0);
|
||||||
|
|
||||||
|
// Pause button
|
||||||
|
const pauseBtn = this.add.text(10, 15, '⏸️', { fontSize: '18px' }).setOrigin(0, 0.5);
|
||||||
|
pauseBtn.setInteractive({ useHandCursor: true });
|
||||||
|
pauseBtn.on('pointerdown', () => this.toggleTimePause());
|
||||||
|
this.timeControlContainer.add(pauseBtn);
|
||||||
|
this.pauseBtn = pauseBtn;
|
||||||
|
}
|
||||||
|
|
||||||
|
setTimeSpeed(speed) {
|
||||||
|
if (this.gameScene && this.gameScene.timeSystem) {
|
||||||
|
this.gameScene.timeSystem.timeScale = speed;
|
||||||
|
console.log(`⏱️ Time speed: ${speed}x`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
highlightSpeedButton(index) {
|
||||||
|
this.timeSpeedButtons.forEach((btn, i) => {
|
||||||
|
if (i === index) {
|
||||||
|
btn.setStyle({ fill: '#ffff00', backgroundColor: '#2a4a6a' });
|
||||||
|
} else {
|
||||||
|
btn.setStyle({ fill: '#4a90e2', backgroundColor: '#1a1a2a' });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleTimePause() {
|
||||||
|
if (!this.gameScene || !this.gameScene.timeSystem) return;
|
||||||
|
|
||||||
|
const ts = this.gameScene.timeSystem;
|
||||||
|
|
||||||
|
if (ts.timeScale === 0) {
|
||||||
|
ts.timeScale = this.lastTimeScale || 1.0;
|
||||||
|
this.pauseBtn.setText('▶️');
|
||||||
|
console.log('▶️ Time resumed');
|
||||||
|
} else {
|
||||||
|
this.lastTimeScale = ts.timeScale;
|
||||||
|
ts.timeScale = 0;
|
||||||
|
this.pauseBtn.setText('⏸️');
|
||||||
|
console.log('⏸️ Time paused');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
toggleBuildMenu(isVisible) {
|
toggleBuildMenu(isVisible) {
|
||||||
if (!this.buildMenuContainer) {
|
if (!this.buildMenuContainer) {
|
||||||
this.createBuildMenuInfo();
|
this.createBuildMenuInfo();
|
||||||
@@ -1897,4 +2091,166 @@ class UIScene extends Phaser.Scene {
|
|||||||
this.scene.start('StoryScene');
|
this.scene.start('StoryScene');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ========== NEW: ZOMBIE & FARM STATS PANELS ==========
|
||||||
|
|
||||||
|
createZombieStatsPanel() {
|
||||||
|
const panelWidth = 220;
|
||||||
|
const panelHeight = 140;
|
||||||
|
const x = 20;
|
||||||
|
const y = 120; // Below player stats
|
||||||
|
|
||||||
|
// Container
|
||||||
|
this.zombieStatsContainer = this.add.container(x, y);
|
||||||
|
this.zombieStatsContainer.setDepth(1000);
|
||||||
|
|
||||||
|
// Background
|
||||||
|
const bg = this.add.graphics();
|
||||||
|
bg.fillStyle(0x1a1a2e, 0.9);
|
||||||
|
bg.fillRect(0, 0, panelWidth, panelHeight);
|
||||||
|
bg.lineStyle(2, 0x8a2be2, 0.8); // Purple border
|
||||||
|
bg.strokeRect(0, 0, panelWidth, panelHeight);
|
||||||
|
this.zombieStatsContainer.add(bg);
|
||||||
|
|
||||||
|
// Title
|
||||||
|
const title = this.add.text(panelWidth / 2, 15, '🧟 ZOMBIE WORKER', {
|
||||||
|
fontSize: '14px',
|
||||||
|
fontFamily: 'Courier New',
|
||||||
|
fill: '#8a2be2',
|
||||||
|
fontStyle: 'bold'
|
||||||
|
}).setOrigin(0.5);
|
||||||
|
this.zombieStatsContainer.add(title);
|
||||||
|
|
||||||
|
// Stats Text
|
||||||
|
this.zombieNameText = this.add.text(10, 35, 'Name: Worker #1', {
|
||||||
|
fontSize: '12px',
|
||||||
|
fill: '#ffffff'
|
||||||
|
});
|
||||||
|
this.zombieStatsContainer.add(this.zombieNameText);
|
||||||
|
|
||||||
|
this.zombieTaskText = this.add.text(10, 55, 'Task: IDLE', {
|
||||||
|
fontSize: '12px',
|
||||||
|
fill: '#ffff00'
|
||||||
|
});
|
||||||
|
this.zombieStatsContainer.add(this.zombieTaskText);
|
||||||
|
|
||||||
|
this.zombieLevelText = this.add.text(10, 75, 'Level: 1 (0/100 XP)', {
|
||||||
|
fontSize: '12px',
|
||||||
|
fill: '#00ff00'
|
||||||
|
});
|
||||||
|
this.zombieStatsContainer.add(this.zombieLevelText);
|
||||||
|
|
||||||
|
// Energy Bar
|
||||||
|
const energyLabel = this.add.text(10, 95, 'ENERGY:', { fontSize: '11px', fill: '#aaaaaa' });
|
||||||
|
this.zombieStatsContainer.add(energyLabel);
|
||||||
|
|
||||||
|
this.zombieEnergyBar = this.drawMiniBar(10, 110, panelWidth - 20, 15, 0x00aaff, 100);
|
||||||
|
this.zombieStatsContainer.add(this.zombieEnergyBar.bg);
|
||||||
|
this.zombieStatsContainer.add(this.zombieEnergyBar.fill);
|
||||||
|
|
||||||
|
// Initially visible
|
||||||
|
this.zombieStatsContainer.setVisible(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
createFarmStatsPanel() {
|
||||||
|
const panelWidth = 220;
|
||||||
|
const panelHeight = 120;
|
||||||
|
const x = 20;
|
||||||
|
const y = 280; // Below zombie stats
|
||||||
|
|
||||||
|
// Container
|
||||||
|
this.farmStatsContainer = this.add.container(x, y);
|
||||||
|
this.farmStatsContainer.setDepth(1000);
|
||||||
|
|
||||||
|
// Background
|
||||||
|
const bg = this.add.graphics();
|
||||||
|
bg.fillStyle(0x1a2e1a, 0.9);
|
||||||
|
bg.fillRect(0, 0, panelWidth, panelHeight);
|
||||||
|
bg.lineStyle(2, 0x00ff00, 0.8); // Green border
|
||||||
|
bg.strokeRect(0, 0, panelWidth, panelHeight);
|
||||||
|
this.farmStatsContainer.add(bg);
|
||||||
|
|
||||||
|
// Title
|
||||||
|
const title = this.add.text(panelWidth / 2, 15, '🌾 FARM STATS', {
|
||||||
|
fontSize: '14px',
|
||||||
|
fontFamily: 'Courier New',
|
||||||
|
fill: '#00ff00',
|
||||||
|
fontStyle: 'bold'
|
||||||
|
}).setOrigin(0.5);
|
||||||
|
this.farmStatsContainer.add(title);
|
||||||
|
|
||||||
|
// Stats
|
||||||
|
this.farmCropsPlantedText = this.add.text(10, 40, 'Crops Planted: 0', {
|
||||||
|
fontSize: '12px',
|
||||||
|
fill: '#ffffff'
|
||||||
|
});
|
||||||
|
this.farmStatsContainer.add(this.farmCropsPlantedText);
|
||||||
|
|
||||||
|
this.farmCropsHarvestedText = this.add.text(10, 60, 'Total Harvested: 0', {
|
||||||
|
fontSize: '12px',
|
||||||
|
fill: '#ffff00'
|
||||||
|
});
|
||||||
|
this.farmStatsContainer.add(this.farmCropsHarvestedText);
|
||||||
|
|
||||||
|
this.farmGoldEarnedText = this.add.text(10, 80, 'Gold Earned: 0g', {
|
||||||
|
fontSize: '12px',
|
||||||
|
fill: '#ffd700'
|
||||||
|
});
|
||||||
|
this.farmStatsContainer.add(this.farmGoldEarnedText);
|
||||||
|
|
||||||
|
this.farmDaysText = this.add.text(10, 100, 'Days Farmed: 0', {
|
||||||
|
fontSize: '12px',
|
||||||
|
fill: '#aaaaaa'
|
||||||
|
});
|
||||||
|
this.farmStatsContainer.add(this.farmDaysText);
|
||||||
|
}
|
||||||
|
|
||||||
|
drawMiniBar(x, y, width, height, color, initialPercent = 100) {
|
||||||
|
// Background
|
||||||
|
const bg = this.add.graphics();
|
||||||
|
bg.fillStyle(0x000000, 0.5);
|
||||||
|
bg.fillRect(x, y, width, height);
|
||||||
|
bg.lineStyle(1, 0xffffff, 0.3);
|
||||||
|
bg.strokeRect(x, y, width, height);
|
||||||
|
|
||||||
|
// Fill
|
||||||
|
const fill = this.add.graphics();
|
||||||
|
fill.fillStyle(color, 1);
|
||||||
|
const maxWidth = width - 4;
|
||||||
|
const currentWidth = (maxWidth * initialPercent) / 100;
|
||||||
|
fill.fillRect(x + 2, y + 2, currentWidth, height - 4);
|
||||||
|
|
||||||
|
return { bg, fill, x, y, width, height, color };
|
||||||
|
}
|
||||||
|
|
||||||
|
setMiniBarValue(bar, percent) {
|
||||||
|
percent = Phaser.Math.Clamp(percent, 0, 100);
|
||||||
|
bar.fill.clear();
|
||||||
|
bar.fill.fillStyle(bar.color, 1);
|
||||||
|
const maxWidth = bar.width - 4;
|
||||||
|
const currentWidth = (maxWidth * percent) / 100;
|
||||||
|
bar.fill.fillRect(bar.x + 2, bar.y + 2, currentWidth, bar.height - 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update methods (call from update())
|
||||||
|
updateZombieStats(zombie) {
|
||||||
|
if (!zombie || !this.zombieStatsContainer) return;
|
||||||
|
|
||||||
|
this.zombieStatsContainer.setVisible(true);
|
||||||
|
if (this.zombieNameText) this.zombieNameText.setText(`Name: ${zombie.name || 'Worker #1'}`);
|
||||||
|
if (this.zombieTaskText) this.zombieTaskText.setText(`Task: ${zombie.task || 'IDLE'}`);
|
||||||
|
if (this.zombieLevelText) this.zombieLevelText.setText(`Level: ${zombie.level || 1} (${zombie.xp || 0}/100 XP)`);
|
||||||
|
|
||||||
|
const energy = zombie.energy !== undefined ? zombie.energy : 100;
|
||||||
|
if (this.zombieEnergyBar) this.setMiniBarValue(this.zombieEnergyBar, energy);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateFarmStats(stats) {
|
||||||
|
if (!stats || !this.farmStatsContainer) return;
|
||||||
|
|
||||||
|
if (this.farmCropsPlantedText) this.farmCropsPlantedText.setText(`Crops Planted: ${stats.cropsPlanted || 0}`);
|
||||||
|
if (this.farmCropsHarvestedText) this.farmCropsHarvestedText.setText(`Total Harvested: ${stats.totalHarvested || 0}`);
|
||||||
|
if (this.farmGoldEarnedText) this.farmGoldEarnedText.setText(`Gold Earned: ${stats.goldEarned || 0}g`);
|
||||||
|
if (this.farmDaysText) this.farmDaysText.setText(`Days Farmed: ${stats.daysFarmed || 0}`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
226
src/systems/BuildSystem.js
Normal file
@@ -0,0 +1,226 @@
|
|||||||
|
class BuildSystem {
|
||||||
|
constructor(scene) {
|
||||||
|
this.scene = scene;
|
||||||
|
this.buildMode = false;
|
||||||
|
this.selectedBuilding = 'fence';
|
||||||
|
this.previewSprite = null;
|
||||||
|
this.placedBuildings = [];
|
||||||
|
|
||||||
|
// Building definitions
|
||||||
|
this.buildings = {
|
||||||
|
'fence': {
|
||||||
|
name: 'Fence (Old)',
|
||||||
|
textureKey: 'fence_isometric',
|
||||||
|
cost: { wood: 2 },
|
||||||
|
collision: false,
|
||||||
|
scale: 0.3
|
||||||
|
},
|
||||||
|
'fence_post': {
|
||||||
|
name: 'Fence Post',
|
||||||
|
textureKey: 'fence_post',
|
||||||
|
cost: { wood: 1 },
|
||||||
|
collision: false,
|
||||||
|
scale: 0.2
|
||||||
|
},
|
||||||
|
'fence_horizontal': {
|
||||||
|
name: 'Fence →',
|
||||||
|
textureKey: 'fence_horizontal',
|
||||||
|
cost: { wood: 2 },
|
||||||
|
collision: false,
|
||||||
|
scale: 0.2
|
||||||
|
},
|
||||||
|
'fence_vertical': {
|
||||||
|
name: 'Fence ↓',
|
||||||
|
textureKey: 'fence_vertical',
|
||||||
|
cost: { wood: 2 },
|
||||||
|
collision: false,
|
||||||
|
scale: 0.2
|
||||||
|
},
|
||||||
|
'fence_corner': {
|
||||||
|
name: 'Fence ⌞',
|
||||||
|
textureKey: 'fence_corner',
|
||||||
|
cost: { wood: 2 },
|
||||||
|
collision: false,
|
||||||
|
scale: 0.2
|
||||||
|
},
|
||||||
|
'barn': {
|
||||||
|
name: 'Barn',
|
||||||
|
textureKey: 'barn_isometric',
|
||||||
|
cost: { wood: 40, stone: 20 },
|
||||||
|
collision: true,
|
||||||
|
scale: 0.5
|
||||||
|
},
|
||||||
|
'grave': {
|
||||||
|
name: 'Grave',
|
||||||
|
textureKey: 'grave_zombie',
|
||||||
|
cost: { stone: 10 },
|
||||||
|
collision: false,
|
||||||
|
scale: 0.3
|
||||||
|
},
|
||||||
|
'farmhouse': {
|
||||||
|
name: 'Farmhouse',
|
||||||
|
textureKey: 'farmhouse_isometric',
|
||||||
|
cost: { wood: 50, stone: 30, gold: 100 },
|
||||||
|
collision: true,
|
||||||
|
scale: 0.5
|
||||||
|
},
|
||||||
|
'blacksmith': {
|
||||||
|
name: 'Blacksmith',
|
||||||
|
textureKey: 'blacksmith_workshop',
|
||||||
|
cost: { wood: 30, stone: 40, gold: 80 },
|
||||||
|
collision: true,
|
||||||
|
scale: 0.45
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleBuildMode() {
|
||||||
|
this.buildMode = !this.buildMode;
|
||||||
|
console.log(`Build Mode: ${this.buildMode ? 'ON' : 'OFF'}`);
|
||||||
|
|
||||||
|
// Notify UI
|
||||||
|
const uiScene = this.scene.scene.get('UIScene');
|
||||||
|
if (uiScene) {
|
||||||
|
uiScene.toggleBuildMenu(this.buildMode);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show/hide preview
|
||||||
|
if (this.buildMode) {
|
||||||
|
this.createPreview();
|
||||||
|
} else {
|
||||||
|
this.destroyPreview();
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.buildMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
selectBuilding(buildingId) {
|
||||||
|
if (!this.buildings[buildingId]) return;
|
||||||
|
this.selectedBuilding = buildingId;
|
||||||
|
|
||||||
|
// Update UI
|
||||||
|
const uiScene = this.scene.scene.get('UIScene');
|
||||||
|
if (uiScene) {
|
||||||
|
uiScene.updateBuildSelection(this.buildings[buildingId].name);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Refresh preview
|
||||||
|
if (this.buildMode) {
|
||||||
|
this.destroyPreview();
|
||||||
|
this.createPreview();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
createPreview() {
|
||||||
|
const building = this.buildings[this.selectedBuilding];
|
||||||
|
if (!this.scene.textures.exists(building.textureKey)) {
|
||||||
|
console.warn(`Texture not found: ${building.textureKey}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.previewSprite = this.scene.add.sprite(0, 0, building.textureKey);
|
||||||
|
this.previewSprite.setOrigin(0.5, 1);
|
||||||
|
this.previewSprite.setAlpha(0.6);
|
||||||
|
this.previewSprite.setDepth(10000); // Always on top
|
||||||
|
this.previewSprite.setScale(building.scale || 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
destroyPreview() {
|
||||||
|
if (this.previewSprite) {
|
||||||
|
this.previewSprite.destroy();
|
||||||
|
this.previewSprite = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
update() {
|
||||||
|
if (!this.buildMode || !this.previewSprite) return;
|
||||||
|
|
||||||
|
// Follow mouse
|
||||||
|
const pointer = this.scene.input.activePointer;
|
||||||
|
const worldPoint = this.scene.cameras.main.getWorldPoint(pointer.x, pointer.y);
|
||||||
|
const gridPos = this.scene.iso.toGrid(worldPoint.x, worldPoint.y);
|
||||||
|
const screenPos = this.scene.iso.toScreen(gridPos.x, gridPos.y);
|
||||||
|
|
||||||
|
this.previewSprite.setPosition(screenPos.x, screenPos.y);
|
||||||
|
|
||||||
|
// Check if can place
|
||||||
|
const canPlace = this.canPlaceAt(gridPos.x, gridPos.y);
|
||||||
|
this.previewSprite.setTint(canPlace ? 0x00ff00 : 0xff0000);
|
||||||
|
|
||||||
|
// Place on click
|
||||||
|
if (pointer.isDown && canPlace) {
|
||||||
|
this.placeBuilding(gridPos.x, gridPos.y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
canPlaceAt(gridX, gridY) {
|
||||||
|
// Check if already has building
|
||||||
|
const exists = this.placedBuildings.find(b => b.gridX === gridX && b.gridY === gridY);
|
||||||
|
if (exists) return false;
|
||||||
|
|
||||||
|
// Check terrain (only on farm tiles)
|
||||||
|
if (!this.scene.terrainSystem) return false;
|
||||||
|
const tile = this.scene.terrainSystem.getTile(gridX, gridY);
|
||||||
|
if (!tile || tile.type !== 'farm') return false;
|
||||||
|
|
||||||
|
// Check cost
|
||||||
|
const building = this.buildings[this.selectedBuilding];
|
||||||
|
if (!this.hasResources(building.cost)) return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
hasResources(cost) {
|
||||||
|
const inv = this.scene.inventorySystem;
|
||||||
|
if (!inv) return false;
|
||||||
|
|
||||||
|
for (const [resource, amount] of Object.entries(cost)) {
|
||||||
|
if (resource === 'gold') {
|
||||||
|
if (inv.gold < amount) return false;
|
||||||
|
} else {
|
||||||
|
if (!inv.hasItem(resource, amount)) return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
placeBuilding(gridX, gridY) {
|
||||||
|
const building = this.buildings[this.selectedBuilding];
|
||||||
|
|
||||||
|
// Consume resources
|
||||||
|
const inv = this.scene.inventorySystem;
|
||||||
|
for (const [resource, amount] of Object.entries(building.cost)) {
|
||||||
|
if (resource === 'gold') {
|
||||||
|
inv.gold -= amount;
|
||||||
|
} else {
|
||||||
|
inv.removeItem(resource, amount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create sprite
|
||||||
|
const screenPos = this.scene.iso.toScreen(gridX, gridY);
|
||||||
|
const sprite = this.scene.add.sprite(screenPos.x, screenPos.y, building.textureKey);
|
||||||
|
sprite.setOrigin(0.5, 1);
|
||||||
|
sprite.setScale(building.scale || 1.0);
|
||||||
|
sprite.setDepth(this.scene.iso.getDepth(gridX, gridY) + 5); // Above terrain
|
||||||
|
|
||||||
|
// Store
|
||||||
|
this.placedBuildings.push({
|
||||||
|
gridX,
|
||||||
|
gridY,
|
||||||
|
type: this.selectedBuilding,
|
||||||
|
sprite,
|
||||||
|
collision: building.collision
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(`🏗️ Placed ${building.name} at (${gridX}, ${gridY})`);
|
||||||
|
|
||||||
|
// Update UI
|
||||||
|
this.scene.events.emit('update-inventory');
|
||||||
|
}
|
||||||
|
|
||||||
|
isCollisionAt(gridX, gridY) {
|
||||||
|
const building = this.placedBuildings.find(b => b.gridX === gridX && b.gridY === gridY);
|
||||||
|
return building ? building.collision : false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,239 +1,216 @@
|
|||||||
const CROP_DATA = {
|
|
||||||
'wheat': {
|
|
||||||
name: 'Wheat',
|
|
||||||
seedItem: 'seeds_wheat',
|
|
||||||
harvestItem: 'wheat',
|
|
||||||
stages: 4,
|
|
||||||
growthTime: 10, // Seconds per stage (Total 30s)
|
|
||||||
regrow: false,
|
|
||||||
color: 0xffdd00
|
|
||||||
},
|
|
||||||
'corn': {
|
|
||||||
name: 'Corn',
|
|
||||||
seedItem: 'seeds_corn',
|
|
||||||
harvestItem: 'corn',
|
|
||||||
stages: 4,
|
|
||||||
growthTime: 15, // Slower (Total 45s)
|
|
||||||
regrow: true, // Corn regrows!
|
|
||||||
regrowStage: 2, // Goes back to stage 2
|
|
||||||
color: 0xffaa00
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class FarmingSystem {
|
class FarmingSystem {
|
||||||
constructor(scene) {
|
constructor(scene) {
|
||||||
this.scene = scene;
|
this.scene = scene;
|
||||||
this.growthTimer = 0;
|
this.crops = []; // Active crops in world
|
||||||
this.growthTickRate = 1000;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Called by InteractionSystem
|
// Crop definitions
|
||||||
interact(gridX, gridY, toolType) {
|
this.cropTypes = {
|
||||||
const terrain = this.scene.terrainSystem;
|
'carrot': {
|
||||||
const tile = terrain.getTile(gridX, gridY);
|
name: 'Carrot',
|
||||||
|
growthStages: 4,
|
||||||
if (!tile) return false;
|
daysPerStage: 1,
|
||||||
|
sellPrice: 10,
|
||||||
// 1. HARVEST
|
seedCost: 5
|
||||||
if (tile.hasCrop) {
|
},
|
||||||
const crop = terrain.cropsMap.get(`${gridX},${gridY}`);
|
'wheat': {
|
||||||
if (crop && crop.stage === CROP_DATA[crop.type].stages) {
|
name: 'Wheat',
|
||||||
this.harvest(gridX, gridY);
|
growthStages: 4,
|
||||||
return true;
|
daysPerStage: 2,
|
||||||
|
sellPrice: 15,
|
||||||
|
seedCost: 8
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// 2. TILLING
|
|
||||||
if (toolType === 'hoe') {
|
|
||||||
const typeName = tile.type.name || tile.type;
|
|
||||||
if (typeName.includes('grass') || typeName === 'dirt') {
|
|
||||||
if (!tile.hasDecoration && !tile.hasCrop) {
|
|
||||||
terrain.setTileType(gridX, gridY, 'farmland');
|
|
||||||
if (this.scene.soundManager) this.scene.soundManager.playDig();
|
|
||||||
|
|
||||||
// Archaeology: Chance to find artefact
|
|
||||||
if (Math.random() < 0.15) {
|
|
||||||
if (this.scene.interactionSystem) { // Using InteractionSystem spawnLoot wrapper or LootSystem directly?
|
|
||||||
// InteractionSystem has spawnLoot method that wraps inventory adding and text.
|
|
||||||
this.scene.interactionSystem.spawnLoot(gridX, gridY, 'artefact_old', 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. PLANTING
|
|
||||||
// Check if tool is a seed
|
|
||||||
const isSeed = toolType.startsWith('seeds_') || toolType === 'seeds';
|
|
||||||
|
|
||||||
if (isSeed) {
|
|
||||||
const typeName = tile.type.name || tile.type;
|
|
||||||
if (typeName === 'farmland' && !tile.hasCrop && !tile.hasDecoration) {
|
|
||||||
// Determine crop type from seed name
|
|
||||||
// "seeds_corn" -> "corn", "seeds" -> "wheat" (default)
|
|
||||||
let cropType = 'wheat';
|
|
||||||
if (toolType === 'seeds_corn') cropType = 'corn';
|
|
||||||
if (toolType === 'seeds_wheat') cropType = 'wheat';
|
|
||||||
|
|
||||||
this.plant(gridX, gridY, cropType);
|
|
||||||
|
|
||||||
// Consumption
|
|
||||||
if (this.scene.inventorySystem) {
|
|
||||||
this.scene.inventorySystem.removeItem(toolType, 1);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 4. WATERING
|
|
||||||
if (toolType === 'watering_can') {
|
|
||||||
if (tile.hasCrop) {
|
|
||||||
const crop = terrain.cropsMap.get(`${gridX},${gridY}`);
|
|
||||||
if (crop && !crop.isWatered) {
|
|
||||||
this.waterCrop(gridX, gridY);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
plant(x, y, type = 'wheat') {
|
|
||||||
const terrain = this.scene.terrainSystem;
|
|
||||||
const data = CROP_DATA[type];
|
|
||||||
|
|
||||||
const cropData = {
|
|
||||||
gridX: x,
|
|
||||||
gridY: y,
|
|
||||||
stage: 1,
|
|
||||||
type: type,
|
|
||||||
timer: 0,
|
|
||||||
maxTime: data.growthTime
|
|
||||||
};
|
};
|
||||||
terrain.addCrop(x, y, cropData);
|
|
||||||
|
|
||||||
if (this.scene.soundManager) {
|
|
||||||
this.scene.soundManager.playPlant();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.scene.questSystem) this.scene.questSystem.trackAction('plant');
|
|
||||||
console.log(`🌱 Planted ${type} at ${x},${y}`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
harvest(x, y) {
|
// Till soil at grid position
|
||||||
const terrain = this.scene.terrainSystem;
|
tillSoil(gridX, gridY) {
|
||||||
const crop = terrain.cropsMap.get(`${x},${y}`);
|
if (!this.scene.terrainSystem) return false;
|
||||||
if (!crop) return;
|
|
||||||
|
|
||||||
const data = CROP_DATA[crop.type];
|
// Check if already tilled
|
||||||
|
const key = `${gridX},${gridY}`;
|
||||||
// Sound
|
if (this.isTilled(gridX, gridY)) {
|
||||||
if (this.scene.soundManager) {
|
console.log('Already tilled!');
|
||||||
this.scene.soundManager.playHarvest();
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Loot
|
// Mark as tilled in terrain
|
||||||
if (this.scene.interactionSystem) {
|
if (!this.scene.terrainSystem.tilledSoil) {
|
||||||
this.scene.interactionSystem.spawnLoot(x, y, data.harvestItem, 1);
|
this.scene.terrainSystem.tilledSoil = new Set();
|
||||||
|
|
||||||
// Chance for seeds
|
|
||||||
if (Math.random() > 0.4) {
|
|
||||||
this.scene.interactionSystem.spawnLoot(x, y, data.seedItem, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extra harvest chance
|
|
||||||
if (Math.random() > 0.5) {
|
|
||||||
this.scene.interactionSystem.spawnLoot(x, y, data.harvestItem, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// XP Gain
|
|
||||||
if (this.scene.statsSystem) {
|
|
||||||
this.scene.statsSystem.addXP(data.harvestItem === 'corn' ? 15 : 10);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Regrow or Destroy
|
this.scene.terrainSystem.tilledSoil.add(key);
|
||||||
if (data.regrow) {
|
|
||||||
crop.stage = data.regrowStage;
|
// Visual feedback - place tilled soil sprite
|
||||||
crop.timer = 0;
|
const screenPos = this.scene.iso.toScreen(gridX, gridY);
|
||||||
crop.isWatered = false; // Reset watering status
|
|
||||||
terrain.updateCropVisual(x, y, crop.stage);
|
if (this.scene.textures.exists('soil_tilled')) {
|
||||||
console.log(`🔄 ${crop.type} regrowing...`);
|
const sprite = this.scene.add.sprite(screenPos.x, screenPos.y, 'soil_tilled');
|
||||||
} else {
|
sprite.setOrigin(0.5, 1);
|
||||||
terrain.removeCrop(x, y);
|
sprite.setScale(1.0);
|
||||||
|
sprite.setDepth(this.scene.iso.getDepth(gridX, gridY) - 1); // Below crops
|
||||||
|
|
||||||
|
// Store reference
|
||||||
|
if (!this.scene.terrainSystem.tilledSprites) {
|
||||||
|
this.scene.terrainSystem.tilledSprites = new Map();
|
||||||
|
}
|
||||||
|
this.scene.terrainSystem.tilledSprites.set(key, sprite);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`✅ Tilled soil at (${gridX}, ${gridY})`);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
isTilled(gridX, gridY) {
|
||||||
|
if (!this.scene.terrainSystem || !this.scene.terrainSystem.tilledSoil) return false;
|
||||||
|
return this.scene.terrainSystem.tilledSoil.has(`${gridX},${gridY}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Plant seed
|
||||||
|
plantSeed(gridX, gridY, cropType) {
|
||||||
|
if (!this.isTilled(gridX, gridY)) {
|
||||||
|
console.log('Need to till soil first!');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if already has crop
|
||||||
|
if (this.getCropAt(gridX, gridY)) {
|
||||||
|
console.log('Already has a crop!');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create crop
|
||||||
|
const crop = {
|
||||||
|
gridX,
|
||||||
|
gridY,
|
||||||
|
type: cropType,
|
||||||
|
stage: 0,
|
||||||
|
daysInStage: 0,
|
||||||
|
plantedDay: this.scene.timeSystem ? this.scene.timeSystem.day : 0
|
||||||
|
};
|
||||||
|
|
||||||
|
this.crops.push(crop);
|
||||||
|
|
||||||
|
// Visual - create sprite
|
||||||
|
this.updateCropSprite(crop);
|
||||||
|
|
||||||
|
// Update farm stats
|
||||||
|
if (this.scene.farmStats) {
|
||||||
|
this.scene.farmStats.cropsPlanted++;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`🌱 Planted ${cropType} at (${gridX}, ${gridY})`);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
getCropAt(gridX, gridY) {
|
||||||
|
return this.crops.find(c => c.gridX === gridX && c.gridY === gridY);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateCropSprite(crop) {
|
||||||
|
const screenPos = this.scene.iso.toScreen(crop.gridX, crop.gridY);
|
||||||
|
|
||||||
|
// Remove old sprite
|
||||||
|
if (crop.sprite) {
|
||||||
|
crop.sprite.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine texture based on stage
|
||||||
|
let textureKey = `crop_${crop.type}_stage_${crop.stage}`;
|
||||||
|
|
||||||
|
// Fallback textures
|
||||||
|
if (!this.scene.textures.exists(textureKey)) {
|
||||||
|
textureKey = 'carrots_stages'; // Use carrot stages as fallback
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.scene.textures.exists(textureKey)) {
|
||||||
|
crop.sprite = this.scene.add.sprite(screenPos.x, screenPos.y, textureKey);
|
||||||
|
crop.sprite.setOrigin(0.5, 1);
|
||||||
|
crop.sprite.setScale(0.8);
|
||||||
|
crop.sprite.setDepth(this.scene.iso.getDepth(crop.gridX, crop.gridY));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
waterCrop(x, y) {
|
// Harvest crop
|
||||||
const terrain = this.scene.terrainSystem;
|
harvestCrop(gridX, gridY) {
|
||||||
const crop = terrain.cropsMap.get(`${x},${y}`);
|
const crop = this.getCropAt(gridX, gridY);
|
||||||
if (!crop) return;
|
if (!crop) return false;
|
||||||
|
|
||||||
crop.isWatered = true;
|
const cropDef = this.cropTypes[crop.type];
|
||||||
crop.growthBoost = 2.0; // 2x faster growth!
|
const isRipe = crop.stage >= (cropDef.growthStages - 1);
|
||||||
|
|
||||||
// Visual feedback - tint slightly blue
|
if (!isRipe) {
|
||||||
const key = `${x},${y}`;
|
console.log('Crop not ready yet!');
|
||||||
if (terrain.visibleCrops.has(key)) {
|
return false;
|
||||||
const sprite = terrain.visibleCrops.get(key);
|
|
||||||
sprite.setTint(0xAADDFF); // Light blue tint
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sound effect
|
// Give items
|
||||||
if (this.scene.soundManager) {
|
if (this.scene.inventorySystem) {
|
||||||
this.scene.soundManager.playPlant(); // Re-use plant sound for now
|
this.scene.inventorySystem.addItem(crop.type, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Floating text
|
// Give gold
|
||||||
this.scene.events.emit('show-floating-text', {
|
const goldEarned = cropDef.sellPrice;
|
||||||
x: x * 48,
|
if (this.scene.inventorySystem) {
|
||||||
y: y * 48,
|
this.scene.inventorySystem.gold += goldEarned;
|
||||||
text: '💧 Watered!',
|
}
|
||||||
color: '#00AAFF'
|
|
||||||
});
|
|
||||||
|
|
||||||
console.log(`💧 Watered crop at ${x},${y}`);
|
// Update farm stats
|
||||||
|
if (this.scene.farmStats) {
|
||||||
|
this.scene.farmStats.totalHarvested++;
|
||||||
|
this.scene.farmStats.goldEarned += goldEarned;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove crop
|
||||||
|
if (crop.sprite) crop.sprite.destroy();
|
||||||
|
this.crops = this.crops.filter(c => c !== crop);
|
||||||
|
|
||||||
|
console.log(`🌾 Harvested ${crop.type}! (+${goldEarned} gold)`);
|
||||||
|
|
||||||
|
// Show floating text
|
||||||
|
if (this.scene.events) {
|
||||||
|
this.scene.events.emit('show-floating-text', {
|
||||||
|
x: screenPos.x,
|
||||||
|
y: screenPos.y - 30,
|
||||||
|
text: `+${goldEarned}g`,
|
||||||
|
color: '#FFD700'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
update(delta) {
|
// Update - called each game day
|
||||||
this.growthTimer += delta;
|
updateDay() {
|
||||||
if (this.growthTimer < 1000) return;
|
this.crops.forEach(crop => {
|
||||||
const secondsPassed = this.growthTimer / 1000;
|
const cropDef = this.cropTypes[crop.type];
|
||||||
this.growthTimer = 0;
|
|
||||||
|
|
||||||
const terrain = this.scene.terrainSystem;
|
crop.daysInStage++;
|
||||||
if (!terrain) return;
|
|
||||||
|
|
||||||
for (const [key, crop] of terrain.cropsMap) {
|
// Advance stage?
|
||||||
const data = CROP_DATA[crop.type];
|
if (crop.daysInStage >= cropDef.daysPerStage) {
|
||||||
if (!data) continue;
|
crop.daysInStage = 0;
|
||||||
|
crop.stage++;
|
||||||
|
|
||||||
if (crop.stage < data.stages) {
|
// Cap at max stage
|
||||||
// Apply growth boost if watered
|
if (crop.stage >= cropDef.growthStages) {
|
||||||
const growthMultiplier = crop.growthBoost || 1.0;
|
crop.stage = cropDef.growthStages - 1;
|
||||||
crop.timer += secondsPassed * growthMultiplier;
|
|
||||||
|
|
||||||
if (crop.timer >= data.growthTime) {
|
|
||||||
crop.stage++;
|
|
||||||
crop.timer = 0;
|
|
||||||
|
|
||||||
// Clear watering boost after growth
|
|
||||||
if (crop.isWatered) {
|
|
||||||
crop.isWatered = false;
|
|
||||||
crop.growthBoost = 1.0;
|
|
||||||
|
|
||||||
// Remove blue tint
|
|
||||||
if (terrain.visibleCrops.has(key)) {
|
|
||||||
const sprite = terrain.visibleCrops.get(key);
|
|
||||||
sprite.clearTint();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
terrain.updateCropVisual(crop.gridX, crop.gridY, crop.stage);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update visual
|
||||||
|
this.updateCropSprite(crop);
|
||||||
|
|
||||||
|
console.log(`🌱 Crop grew! Stage ${crop.stage}/${cropDef.growthStages - 1}`);
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update - called each frame
|
||||||
|
update() {
|
||||||
|
// Update sprite depths if camera moved
|
||||||
|
this.crops.forEach(crop => {
|
||||||
|
if (crop.sprite) {
|
||||||
|
crop.sprite.setDepth(this.scene.iso.getDepth(crop.gridX, crop.gridY));
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ class InteractionSystem {
|
|||||||
|
|
||||||
handleTalk(npc) {
|
handleTalk(npc) {
|
||||||
if (!this.scene.hybridSkillSystem) {
|
if (!this.scene.hybridSkillSystem) {
|
||||||
npc.toggleState();
|
console.log('Talk with:', npc.type);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -120,7 +120,7 @@ class InteractionSystem {
|
|||||||
color: color
|
color: color
|
||||||
});
|
});
|
||||||
|
|
||||||
npc.toggleState(); // Stop moving briefly
|
// NPC pauses briefly (handled by AI)
|
||||||
}
|
}
|
||||||
|
|
||||||
handleInteraction(gridX, gridY, isAttack = false) {
|
handleInteraction(gridX, gridY, isAttack = false) {
|
||||||
@@ -247,7 +247,7 @@ class InteractionSystem {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isAttack) npc.toggleState();
|
// NPC interaction complete
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -301,11 +301,8 @@ class InteractionSystem {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4. Try Farming Action
|
// 4. Farming is now handled via Player Space key (handleFarmingAction)
|
||||||
if (this.scene.farmingSystem && !isAttack) {
|
// No click farming anymore
|
||||||
const didFarm = this.scene.farmingSystem.interact(gridX, gridY, activeTool);
|
|
||||||
if (didFarm) return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 5. Try damage decoration
|
// 5. Try damage decoration
|
||||||
const id = `${gridX},${gridY}`;
|
const id = `${gridX},${gridY}`;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
// ========================================================
|
// ========================================================
|
||||||
// NOVE GLOBALNE KONSTANTE ZA LOKACIJE
|
// NOVE GLOBALNE KONSTANTE ZA LOKACIJE
|
||||||
// ========================================================
|
// ========================================================
|
||||||
const FARM_SIZE = 100; // 100x100 VELIKA PLATFORMA!
|
const FARM_SIZE = 8; // 8x8 MICRO FARM - Začetek igre!
|
||||||
const FARM_CENTER_X = 50; // Center mape (50,50)
|
const FARM_CENTER_X = 50; // Center mape (50,50)
|
||||||
const FARM_CENTER_Y = 50; // Center mape (50,50)
|
const FARM_CENTER_Y = 50; // Center mape (50,50)
|
||||||
|
|
||||||
@@ -453,6 +453,7 @@ class TerrainSystem {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// --- CHUNK DECORATION PASS ---
|
// --- CHUNK DECORATION PASS ---
|
||||||
// DISABLED - User wants clean platform (no trees, rocks, flowers)
|
// DISABLED - User wants clean platform (no trees, rocks, flowers)
|
||||||
|
|
||||||
@@ -477,6 +478,81 @@ class TerrainSystem {
|
|||||||
// this.addDecoration(pos.x, pos.y, 'path_stone');
|
// this.addDecoration(pos.x, pos.y, 'path_stone');
|
||||||
// }
|
// }
|
||||||
// });
|
// });
|
||||||
|
|
||||||
|
// MICRO FARM FENCE (8x8 starting area) - DISABLED FOR NOW
|
||||||
|
// Will be added properly later
|
||||||
|
const farmMinX = FARM_CENTER_X - FARM_SIZE / 2;
|
||||||
|
const farmMaxX = FARM_CENTER_X + FARM_SIZE / 2;
|
||||||
|
const farmMinY = FARM_CENTER_Y - FARM_SIZE / 2;
|
||||||
|
const farmMaxY = FARM_CENTER_Y + FARM_SIZE / 2;
|
||||||
|
|
||||||
|
// DECORATIONS: Flowers & Bushes on grass tiles (random)
|
||||||
|
for (let y = startY; y < endY; y++) {
|
||||||
|
for (let x = startX; x < endX; x++) {
|
||||||
|
const tile = this.tiles[y][x]; // Corrected access to this.tiles
|
||||||
|
|
||||||
|
// Only on grass tiles, outside farm area
|
||||||
|
if (tile && tile.type === 'GRASS_FULL' && // Changed 'grass' to 'GRASS_FULL' to match terrainType.name
|
||||||
|
(x < farmMinX || x > farmMaxX || y < farmMinY || y > farmMaxY)) {
|
||||||
|
|
||||||
|
const rand = Math.random();
|
||||||
|
|
||||||
|
// 10% chance for flowers
|
||||||
|
if (rand < 0.10) {
|
||||||
|
const flowerTypes = ['flowers_new', 'flowers_pink_isometric'];
|
||||||
|
const randomType = flowerTypes[Math.floor(Math.random() * flowerTypes.length)];
|
||||||
|
this.addDecoration(x, y, randomType);
|
||||||
|
}
|
||||||
|
// 8% chance for grass patches (visual variety)
|
||||||
|
else if (rand >= 0.10 && rand < 0.18) {
|
||||||
|
this.addDecoration(x, y, 'grass_sprite');
|
||||||
|
}
|
||||||
|
// 5% chance for bushes
|
||||||
|
else if (rand >= 0.18 && rand < 0.23) {
|
||||||
|
this.addDecoration(x, y, 'bush_small');
|
||||||
|
}
|
||||||
|
// 3% chance for small rocks
|
||||||
|
else if (rand >= 0.23 && rand < 0.26) {
|
||||||
|
const rockTypes = ['rock_small', 'rock_2'];
|
||||||
|
const randomRock = rockTypes[Math.floor(Math.random() * rockTypes.length)];
|
||||||
|
this.addDecoration(x, y, randomRock);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if this chunk contains farm border
|
||||||
|
if (cx * this.chunkSize <= farmMaxX && (cx + 1) * this.chunkSize >= farmMinX &&
|
||||||
|
cy * this.chunkSize <= farmMaxY && (cy + 1) * this.chunkSize >= farmMinY) {
|
||||||
|
|
||||||
|
// Top border (y = farmMinY)
|
||||||
|
if (startY <= farmMinY && endY > farmMinY) {
|
||||||
|
for (let x = Math.max(startX, farmMinX); x <= Math.min(endX - 1, farmMaxX); x++) {
|
||||||
|
this.addDecoration(x, farmMinY, 'fence_isometric');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bottom border (y = farmMaxY)
|
||||||
|
if (startY <= farmMaxY && endY > farmMaxY) {
|
||||||
|
for (let x = Math.max(startX, farmMinX); x <= Math.min(endX - 1, farmMaxX); x++) {
|
||||||
|
this.addDecoration(x, farmMaxY, 'fence_isometric');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Left border (x = farmMinX)
|
||||||
|
if (startX <= farmMinX && endX > farmMinX) {
|
||||||
|
for (let y = Math.max(startY, farmMinY); y <= Math.min(endY - 1, farmMaxY); y++) {
|
||||||
|
this.addDecoration(farmMinX, y, 'fence_isometric');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Right border (x = farmMaxX)
|
||||||
|
if (startX <= farmMaxX && endX > farmMaxX) {
|
||||||
|
for (let y = Math.max(startY, farmMinY); y <= Math.min(endY - 1, farmMaxY); y++) {
|
||||||
|
this.addDecoration(farmMaxX, y, 'fence_isometric');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
updateChunks(camera) {
|
updateChunks(camera) {
|
||||||
@@ -732,14 +808,15 @@ class TerrainSystem {
|
|||||||
typeLower.includes('small_rock') ||
|
typeLower.includes('small_rock') ||
|
||||||
typeLower.includes('path_stone') ||
|
typeLower.includes('path_stone') ||
|
||||||
typeLower.includes('mushroom') ||
|
typeLower.includes('mushroom') ||
|
||||||
typeLower.includes('puddle');
|
typeLower.includes('puddle') ||
|
||||||
|
typeLower.includes('fence'); // OGRAJE SO PREHODNE!
|
||||||
|
|
||||||
const isSolid = !isSmallDecor && (
|
const isSolid = !isSmallDecor && (
|
||||||
typeLower.includes('tree') ||
|
typeLower.includes('tree') ||
|
||||||
typeLower.includes('sapling') ||
|
typeLower.includes('sapling') ||
|
||||||
typeLower.includes('rock') ||
|
typeLower.includes('rock') ||
|
||||||
typeLower.includes('stone') ||
|
typeLower.includes('stone') ||
|
||||||
typeLower.includes('fence') ||
|
// fence REMOVED - it's walkable now!
|
||||||
typeLower.includes('wall') ||
|
typeLower.includes('wall') ||
|
||||||
typeLower.includes('signpost') ||
|
typeLower.includes('signpost') ||
|
||||||
typeLower.includes('hill') ||
|
typeLower.includes('hill') ||
|
||||||
|
|||||||
58
tools/create_spritesheet.js
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
// Spritesheet Creator - combines 6 individual frames into horizontal spritesheet
|
||||||
|
const { createCanvas, loadImage } = require('canvas');
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
async function createSpritesheet(frames, outputPath) {
|
||||||
|
const frameWidth = 64;
|
||||||
|
const frameHeight = 64;
|
||||||
|
const totalWidth = frameWidth * frames.length;
|
||||||
|
|
||||||
|
const canvas = createCanvas(totalWidth, frameHeight);
|
||||||
|
const ctx = canvas.getContext('2d');
|
||||||
|
|
||||||
|
console.log(`Creating spritesheet: ${totalWidth}x${frameHeight}`);
|
||||||
|
|
||||||
|
for (let i = 0; i < frames.length; i++) {
|
||||||
|
try {
|
||||||
|
const img = await loadImage(frames[i]);
|
||||||
|
ctx.drawImage(img, i * frameWidth, 0, frameWidth, frameHeight);
|
||||||
|
console.log(` Frame ${i + 1}: ${path.basename(frames[i])}`);
|
||||||
|
} catch (err) {
|
||||||
|
console.error(`Error loading frame ${i + 1}:`, err.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const buffer = canvas.toBuffer('image/png');
|
||||||
|
fs.writeFileSync(outputPath, buffer);
|
||||||
|
console.log(`✅ Spritesheet saved: ${outputPath}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Define frame paths
|
||||||
|
const playerFrames = [
|
||||||
|
'C:/Users/hipod/.gemini/antigravity/brain/c3cb08b5-19b4-4254-a001-eeab1394eb46/player_frame_1_idle_1765466652332.png',
|
||||||
|
'C:/Users/hipod/.gemini/antigravity/brain/c3cb08b5-19b4-4254-a001-eeab1394eb46/player_frame_2_left_1765466686157.png',
|
||||||
|
'C:/Users/hipod/.gemini/antigravity/brain/c3cb08b5-19b4-4254-a001-eeab1394eb46/player_frame_3_mid_left_1765466722379.png',
|
||||||
|
'C:/Users/hipod/.gemini/antigravity/brain/c3cb08b5-19b4-4254-a001-eeab1394eb46/player_frame_4_right_1765466767612.png',
|
||||||
|
'C:/Users/hipod/.gemini/antigravity/brain/c3cb08b5-19b4-4254-a001-eeab1394eb46/player_frame_5_mid_right_1765466812029.png',
|
||||||
|
'C:/Users/hipod/.gemini/antigravity/brain/c3cb08b5-19b4-4254-a001-eeab1394eb46/player_frame_6_idle_return_1765466859078.png'
|
||||||
|
];
|
||||||
|
|
||||||
|
const zombieFrames = [
|
||||||
|
'C:/Users/hipod/.gemini/antigravity/brain/c3cb08b5-19b4-4254-a001-eeab1394eb46/zombie_frame_1_idle_1765466924235.png',
|
||||||
|
'C:/Users/hipod/.gemini/antigravity/brain/c3cb08b5-19b4-4254-a001-eeab1394eb46/zombie_frame_2_left_1765466975297.png',
|
||||||
|
'C:/Users/hipod/.gemini/antigravity/brain/c3cb08b5-19b4-4254-a001-eeab1394eb46/zombie_frame_3_mid_left_1765467026661.png',
|
||||||
|
'C:/Users/hipod/.gemini/antigravity/brain/c3cb08b5-19b4-4254-a001-eeab1394eb46/zombie_frame_4_right_1765467064996.png',
|
||||||
|
'C:/Users/hipod/.gemini/antigravity/brain/c3cb08b5-19b4-4254-a001-eeab1394eb46/zombie_frame_5_mid_right_1765467114381.png',
|
||||||
|
'C:/Users/hipod/.gemini/antigravity/brain/c3cb08b5-19b4-4254-a001-eeab1394eb46/zombie_frame_6_idle_return_1765467152088.png'
|
||||||
|
];
|
||||||
|
|
||||||
|
// Run
|
||||||
|
(async () => {
|
||||||
|
console.log('🎬 Creating spritesheets...\n');
|
||||||
|
|
||||||
|
await createSpritesheet(playerFrames, 'c:/novafarma/assets/player_dreadlocks.png');
|
||||||
|
await createSpritesheet(zombieFrames, 'c:/novafarma/assets/zombie_worker.png');
|
||||||
|
|
||||||
|
console.log('\n✅ ALL DONE!');
|
||||||
|
})();
|
||||||
69
tools/farming_controls_template.js
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
// FARMING CONTROLS - Add to Player.js
|
||||||
|
|
||||||
|
// In handleAction() method, add this code:
|
||||||
|
|
||||||
|
handleFarmingAction() {
|
||||||
|
if (!this.scene.farmingSystem) return;
|
||||||
|
|
||||||
|
const selectedSlot = this.scene.scene.get('UIScene').selectedSlot;
|
||||||
|
const inventory = this.scene.inventorySystem;
|
||||||
|
const item = inventory ? inventory.slots[selectedSlot] : null;
|
||||||
|
const itemType = item ? item.type : null;
|
||||||
|
|
||||||
|
// Get grid position player is facing
|
||||||
|
const facingX = this.gridX;
|
||||||
|
const facingY = this.gridY;
|
||||||
|
|
||||||
|
// Adjust based on direction (optional - or use mouse click position)
|
||||||
|
// For now, use tile player is standing on
|
||||||
|
|
||||||
|
// HOE - Till soil
|
||||||
|
if (itemType === 'hoe') {
|
||||||
|
const success = this.scene.farmingSystem.tillSoil(facingX, facingY);
|
||||||
|
if (success) {
|
||||||
|
// Play sound/animation
|
||||||
|
if (this.scene.soundManager) this.scene.soundManager.playDig();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// SEEDS - Plant
|
||||||
|
if (itemType === 'carrot' || itemType === 'wheat') {
|
||||||
|
const success = this.scene.farmingSystem.plantSeed(facingX, facingY, itemType);
|
||||||
|
if (success) {
|
||||||
|
// Consume seed
|
||||||
|
inventory.removeItem(itemType, 1);
|
||||||
|
if (this.scene.soundManager) this.scene.soundManager.playPlant();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// EMPTY HAND - Harvest
|
||||||
|
if (!itemType) {
|
||||||
|
const success = this.scene.farmingSystem.harvestCrop(facingX, facingY);
|
||||||
|
if (success) {
|
||||||
|
if (this.scene.soundManager) this.scene.soundManager.playHarvest();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// In Player update(), listen for SPACE or MOUSE CLICK:
|
||||||
|
// Add to existing input handling:
|
||||||
|
|
||||||
|
/*
|
||||||
|
if (this.scene.input.keyboard.checkDown(this.scene.input.keyboard.addKey('SPACE'), 300)) {
|
||||||
|
this.handleFarmingAction();
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
// OR use mouse click on tile:
|
||||||
|
/*
|
||||||
|
this.scene.input.on('pointerdown', (pointer) => {
|
||||||
|
const worldPoint = this.scene.cameras.main.getWorldPoint(pointer.x, pointer.y);
|
||||||
|
const gridPos = this.scene.iso.toGrid(worldPoint.x, worldPoint.y);
|
||||||
|
|
||||||
|
// Farming action at clicked tile
|
||||||
|
this.farmAtTile(gridPos.x, gridPos.y);
|
||||||
|
});
|
||||||
|
*/
|
||||||
109
tools/time_control_panel.js
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
// Time Speed Control Panel
|
||||||
|
// Add to UIScene.js
|
||||||
|
|
||||||
|
createTimeControlPanel() {
|
||||||
|
const x = this.scale.width - 170;
|
||||||
|
const y = 250; // Below resources
|
||||||
|
|
||||||
|
// Container
|
||||||
|
this.timeControlContainer = this.add.container(x, y);
|
||||||
|
this.timeControlContainer.setDepth(1000);
|
||||||
|
|
||||||
|
// Background
|
||||||
|
const bg = this.add.graphics();
|
||||||
|
bg.fillStyle(0x1a1a2a, 0.8);
|
||||||
|
bg.fillRect(0, 0, 150, 100);
|
||||||
|
bg.lineStyle(2, 0x4a90e2, 0.8);
|
||||||
|
bg.strokeRect(0, 0, 150, 100);
|
||||||
|
this.timeControlContainer.add(bg);
|
||||||
|
|
||||||
|
// Title
|
||||||
|
const title = this.add.text(75, 15, 'TIME SPEED', {
|
||||||
|
fontSize: '12px',
|
||||||
|
fontFamily: 'Courier New',
|
||||||
|
fill: '#ffffff',
|
||||||
|
fontStyle: 'bold'
|
||||||
|
}).setOrigin(0.5, 0.5);
|
||||||
|
this.timeControlContainer.add(title);
|
||||||
|
|
||||||
|
// Speed buttons
|
||||||
|
const speeds = [
|
||||||
|
{ label: '1x', value: 1.0, y: 40 },
|
||||||
|
{ label: '2x', value: 2.0, y: 60 },
|
||||||
|
{ label: '5x', value: 5.0, y: 80 }
|
||||||
|
];
|
||||||
|
|
||||||
|
this.timeSpeedButtons = [];
|
||||||
|
|
||||||
|
speeds.forEach((speed, i) => {
|
||||||
|
const btn = this.add.text(75, speed.y, speed.label, {
|
||||||
|
fontSize: '14px',
|
||||||
|
fontFamily: 'Courier New',
|
||||||
|
fill: '#4a90e2',
|
||||||
|
fontStyle: 'bold',
|
||||||
|
backgroundColor: '#1a1a2a',
|
||||||
|
padding: { x: 20, y: 5 }
|
||||||
|
}).setOrigin(0.5, 0.5);
|
||||||
|
|
||||||
|
btn.setInteractive({ useHandCursor: true });
|
||||||
|
btn.on('pointerdown', () => {
|
||||||
|
this.setTimeSpeed(speed.value);
|
||||||
|
this.highlightSpeedButton(i);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.timeControlContainer.add(btn);
|
||||||
|
this.timeSpeedButtons.push(btn);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Highlight default (1x)
|
||||||
|
this.highlightSpeedButton(0);
|
||||||
|
|
||||||
|
// Pause button
|
||||||
|
const pauseBtn = this.add.text(10, 15, '⏸️', {
|
||||||
|
fontSize: '18px'
|
||||||
|
}).setOrigin(0, 0.5);
|
||||||
|
|
||||||
|
pauseBtn.setInteractive({ useHandCursor: true });
|
||||||
|
pauseBtn.on('pointerdown', () => {
|
||||||
|
this.toggleTimePause();
|
||||||
|
});
|
||||||
|
|
||||||
|
this.timeControlContainer.add(pauseBtn);
|
||||||
|
this.pauseBtn = pauseBtn;
|
||||||
|
}
|
||||||
|
|
||||||
|
setTimeSpeed(speed) {
|
||||||
|
if (this.gameScene && this.gameScene.timeSystem) {
|
||||||
|
this.gameScene.timeSystem.timeScale = speed;
|
||||||
|
console.log(`⏱️ Time speed: ${speed}x`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
highlightSpeedButton(index) {
|
||||||
|
this.timeSpeedButtons.forEach((btn, i) => {
|
||||||
|
if (i === index) {
|
||||||
|
btn.setStyle({ fill: '#ffff00', backgroundColor: '#2a4a6a' });
|
||||||
|
} else {
|
||||||
|
btn.setStyle({ fill: '#4a90e2', backgroundColor: '#1a1a2a' });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleTimePause() {
|
||||||
|
if (!this.gameScene || !this.gameScene.timeSystem) return;
|
||||||
|
|
||||||
|
const ts = this.gameScene.timeSystem;
|
||||||
|
|
||||||
|
if (ts.timeScale === 0) {
|
||||||
|
// Unpause
|
||||||
|
ts.timeScale = this.lastTimeScale || 1.0;
|
||||||
|
this.pauseBtn.setText('⏸️');
|
||||||
|
console.log('▶️ Time resumed');
|
||||||
|
} else {
|
||||||
|
// Pause
|
||||||
|
this.lastTimeScale = ts.timeScale;
|
||||||
|
ts.timeScale = 0;
|
||||||
|
this.pauseBtn.setText('▶️');
|
||||||
|
console.log('⏸️ Time paused');
|
||||||
|
}
|
||||||
|
}
|
||||||
160
tools/ui_panels_code.js
Normal file
@@ -0,0 +1,160 @@
|
|||||||
|
// ZOMBIE & FARM STATS PANELS - Add to UIScene.js
|
||||||
|
|
||||||
|
// Call this in create() method:
|
||||||
|
createZombieStatsPanel() {
|
||||||
|
const panelWidth = 220;
|
||||||
|
const panelHeight = 140;
|
||||||
|
const x = 20;
|
||||||
|
const y = 120; // Below player stats
|
||||||
|
|
||||||
|
// Container
|
||||||
|
this.zombieStatsContainer = this.add.container(x, y);
|
||||||
|
this.zombieStatsContainer.setDepth(1000);
|
||||||
|
|
||||||
|
// Background
|
||||||
|
const bg = this.add.graphics();
|
||||||
|
bg.fillStyle(0x1a1a2e, 0.9);
|
||||||
|
bg.fillRect(0, 0, panelWidth, panelHeight);
|
||||||
|
bg.lineStyle(2, 0x8a2be2, 0.8); // Purple border (zombie theme)
|
||||||
|
bg.strokeRect(0, 0, panelWidth, panelHeight);
|
||||||
|
this.zombieStatsContainer.add(bg);
|
||||||
|
|
||||||
|
// Title
|
||||||
|
const title = this.add.text(panelWidth / 2, 15, '🧟 ZOMBIE WORKER', {
|
||||||
|
fontSize: '14px',
|
||||||
|
fontFamily: 'Courier New',
|
||||||
|
fill: '#8a2be2',
|
||||||
|
fontStyle: 'bold'
|
||||||
|
}).setOrigin(0.5);
|
||||||
|
this.zombieStatsContainer.add(title);
|
||||||
|
|
||||||
|
// Stats Text
|
||||||
|
this.zombieNameText = this.add.text(10, 35, 'Name: Worker #1', {
|
||||||
|
fontSize: '12px',
|
||||||
|
fill: '#ffffff'
|
||||||
|
});
|
||||||
|
this.zombieStatsContainer.add(this.zombieNameText);
|
||||||
|
|
||||||
|
this.zombieTaskText = this.add.text(10, 55, 'Task: IDLE', {
|
||||||
|
fontSize: '12px',
|
||||||
|
fill: '#ffff00'
|
||||||
|
});
|
||||||
|
this.zombieStatsContainer.add(this.zombieTaskText);
|
||||||
|
|
||||||
|
this.zombieLevelText = this.add.text(10, 75, 'Level: 1 (0/100 XP)', {
|
||||||
|
fontSize: '12px',
|
||||||
|
fill: '#00ff00'
|
||||||
|
});
|
||||||
|
this.zombieStatsContainer.add(this.zombieLevelText);
|
||||||
|
|
||||||
|
// Energy Bar
|
||||||
|
this.add.text(10, 95, 'ENERGY:', { fontSize: '11px', fill: '#aaaaaa' });
|
||||||
|
this.zombieEnergyBar = this.drawMiniBar(10, 110, panelWidth - 20, 15, 0x00aaff, 100);
|
||||||
|
this.zombieStatsContainer.add(this.zombieEnergyBar.bg);
|
||||||
|
this.zombieStatsContainer.add(this.zombieEnergyBar.fill);
|
||||||
|
|
||||||
|
// Initially hidden - shows when zombie is selected/nearby
|
||||||
|
this.zombieStatsContainer.setVisible(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
createFarmStatsPanel() {
|
||||||
|
const panelWidth = 220;
|
||||||
|
const panelHeight = 120;
|
||||||
|
const x = 20;
|
||||||
|
const y = 280; // Below zombie stats
|
||||||
|
|
||||||
|
// Container
|
||||||
|
this.farmStatsContainer = this.add.container(x, y);
|
||||||
|
this.farmStatsContainer.setDepth(1000);
|
||||||
|
|
||||||
|
// Background
|
||||||
|
const bg = this.add.graphics();
|
||||||
|
bg.fillStyle(0x1a2e1a, 0.9);
|
||||||
|
bg.fillRect(0, 0, panelWidth, panelHeight);
|
||||||
|
bg.lineStyle(2, 0x00ff00, 0.8); // Green border (farm theme)
|
||||||
|
bg.strokeRect(0, 0, panelWidth, panelHeight);
|
||||||
|
this.farmStatsContainer.add(bg);
|
||||||
|
|
||||||
|
// Title
|
||||||
|
const title = this.add.text(panelWidth / 2, 15, '🌾 FARM STATS', {
|
||||||
|
fontSize: '14px',
|
||||||
|
fontFamily: 'Courier New',
|
||||||
|
fill: '#00ff00',
|
||||||
|
fontStyle: 'bold'
|
||||||
|
}).setOrigin(0.5);
|
||||||
|
this.farmStatsContainer.add(title);
|
||||||
|
|
||||||
|
// Stats
|
||||||
|
this.farmCropsPlantedText = this.add.text(10, 40, 'Crops Planted: 0', {
|
||||||
|
fontSize: '12px',
|
||||||
|
fill: '#ffffff'
|
||||||
|
});
|
||||||
|
this.farmStatsContainer.add(this.farmCropsPlantedText);
|
||||||
|
|
||||||
|
this.farmCropsHarvestedText = this.add.text(10, 60, 'Total Harvested: 0', {
|
||||||
|
fontSize: '12px',
|
||||||
|
fill: '#ffff00'
|
||||||
|
});
|
||||||
|
this.farmStatsContainer.add(this.farmCropsHarvestedText);
|
||||||
|
|
||||||
|
this.farmGoldEarnedText = this.add.text(10, 80, 'Gold Earned: 0g', {
|
||||||
|
fontSize: '12px',
|
||||||
|
fill: '#ffd700'
|
||||||
|
});
|
||||||
|
this.farmStatsContainer.add(this.farmGoldEarnedText);
|
||||||
|
|
||||||
|
this.farmDaysText = this.add.text(10, 100, 'Days Farmed: 0', {
|
||||||
|
fontSize: '12px',
|
||||||
|
fill: '#aaaaaa'
|
||||||
|
});
|
||||||
|
this.farmStatsContainer.add(this.farmDaysText);
|
||||||
|
}
|
||||||
|
|
||||||
|
drawMiniBar(x, y, width, height, color, initialPercent = 100) {
|
||||||
|
// Background
|
||||||
|
const bg = this.add.graphics();
|
||||||
|
bg.fillStyle(0x000000, 0.5);
|
||||||
|
bg.fillRect(x, y, width, height);
|
||||||
|
bg.lineStyle(1, 0xffffff, 0.3);
|
||||||
|
bg.strokeRect(x, y, width, height);
|
||||||
|
|
||||||
|
// Fill
|
||||||
|
const fill = this.add.graphics();
|
||||||
|
fill.fillStyle(color, 1);
|
||||||
|
const maxWidth = width - 4;
|
||||||
|
const currentWidth = (maxWidth * initialPercent) / 100;
|
||||||
|
fill.fillRect(x + 2, y + 2, currentWidth, height - 4);
|
||||||
|
|
||||||
|
return { bg, fill, x, y, width, height, color };
|
||||||
|
}
|
||||||
|
|
||||||
|
setMiniBarValue(bar, percent) {
|
||||||
|
percent = Phaser.Math.Clamp(percent, 0, 100);
|
||||||
|
bar.fill.clear();
|
||||||
|
bar.fill.fillStyle(bar.color, 1);
|
||||||
|
const maxWidth = bar.width - 4;
|
||||||
|
const currentWidth = (maxWidth * percent) / 100;
|
||||||
|
bar.fill.fillRect(bar.x + 2, bar.y + 2, currentWidth, bar.height - 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update methods (call from update())
|
||||||
|
updateZombieStats(zombie) {
|
||||||
|
if (!zombie || !this.zombieStatsContainer) return;
|
||||||
|
|
||||||
|
this.zombieStatsContainer.setVisible(true);
|
||||||
|
this.zombieNameText.setText(`Name: ${zombie.name || 'Worker #1'}`);
|
||||||
|
this.zombieTaskText.setText(`Task: ${zombie.task || 'IDLE'}`);
|
||||||
|
this.zombieLevelText.setText(`Level: ${zombie.level || 1} (${zombie.xp || 0}/100 XP)`);
|
||||||
|
|
||||||
|
const energy = zombie.energy !== undefined ? zombie.energy : 100;
|
||||||
|
this.setMiniBarValue(this.zombieEnergyBar, energy);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateFarmStats(stats) {
|
||||||
|
if (!stats || !this.farmStatsContainer) return;
|
||||||
|
|
||||||
|
this.farmCropsPlantedText.setText(`Crops Planted: ${stats.cropsPlanted || 0}`);
|
||||||
|
this.farmCropsHarvestedText.setText(`Total Harvested: ${stats.totalHarvested || 0}`);
|
||||||
|
this.farmGoldEarnedText.setText(`Gold Earned: ${stats.goldEarned || 0}g`);
|
||||||
|
this.farmDaysText.setText(`Days Farmed: ${stats.daysFarmed || 0}`);
|
||||||
|
}
|
||||||