diff --git a/CHANGELOG.md b/CHANGELOG.md
index 87b3035..126d1f2 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,124 +1,159 @@
-# NovaFarma - Changelog
+# CHANGELOG - NovaFarma Development
-## Version 2.5.0 (2025-12-08)
+## [Session: 8.12.2025] - Phase 13 & Polish
-### ๐ฎ Major Features
+### โ
IMPLEMENTIRANO
-#### **Elite Zombies** ๐น
-- Added Elite Zombie enemy type (dark red with glowing pink eyes)
-- 50 HP (2.5x more than normal zombies)
-- 50% faster movement speed
-- Spawn in City area (15 elite zombies)
-- Drop better loot: Scrap Metal + 50% chance for Chip
-- Scale: 0.2 (smaller but deadlier)
+#### ๐๏ธ **City Content & Combat Polish**
+- **Unique City Loot**
+ - Added `scrap_metal` (5 units, 80% chance) to city chests
+ - Added `chips` (electronics, 2 units, 60% chance) to city chests
+ - Added `scrap_metal` (15 units) and `chips` (5 units, 90% chance) to elite chests
+
+- **Combat Visual Feedback**
+ - `NPC.takeDamage(amount, attacker)` - Full implementation
+ - โจ White flash effect on hit (100ms)
+ - ๐ฏ Knockback physics (0.5 tile pushback)
+ - ๐ฅ Floating damage text (`-HP` in red)
+ - ๐จ Color-coded health bars (green โ orange โ red)
+ - ๐ Fade-out death animation (300ms)
-#### **City Content** ๐๏ธ
-- **City Wall (Obzidje)**: 15x15 fortified city with stone walls
- - WALL_EDGE tiles on perimeter (solid collision)
- - Pavement interior with ruins
-- **Treasure Chests**: 3 chests in ruins with random loot (2-4 items)
- - Interact with 'E' key to open
- - Loot: scrap, chips, wood, stone, bones
-- **Zombie Spawners**: 2 dark portals in city
- - Visual: Dark stone with red glow
-- **5 Ruin Structures**: Scattered throughout city
-- **Arena**: Boss battle area at (75, 55)
+#### ๐ก๏ธ **Weather System v2.0 - Temperature & Survival**
+- **Seasonal Temperature System**
+ - Spring: 15ยฐC (safe)
+ - Summer: 30ยฐC base
+ - Autumn: 10ยฐC (safe)
+ - Winter: -5ยฐC base
+ - Day/night variation: ยฑ5ยฐC (sine wave)
+
+- **Survival Mechanics**
+ - Cold damage: `Temp < 0ยฐC` โ -5 HP every 5s (without Winter Coat)
+ - Heat damage: `Temp > 35ยฐC` โ -3 HP every 5s (without Summer Hat)
+ - Protection items: `winter_coat`, `summer_hat`
+ - Visual indicators: โ๏ธ Freezing! / ๐ฅ Overheating!
-#### **Roads & Navigation** ๐ฃ๏ธ
-- **Gray Stone Roads**: Connecting Farm (20,20) and City (65,65)
- - Horizontal road from farm
- - Vertical road to city
- - L-shaped path
-- **Signposts**: 3 wooden navigation markers
- - Farm signpost: โ (points to city)
- - City signpost: โ (points to farm)
- - Crossroads signpost: โ
(both directions)
+- **Greenhouse Building**
+ - Recipe: 20 Glass + 15 Wood
+ - Size: 2x2 tiles
+ - Purpose: Enables winter farming
+ - Glass Crafting: Sand + Coal โ Glass (Furnace, 3000ms)
-#### **Farm Starter Zone** ๐พ
-- **Cleared Farm Area**: 16x16 tiles at (20, 20)
- - All trees and rocks removed
- - Green grass terrain
-- **Starter Resources**: Treasure chest with initial items
-- **Fence Posts**: 4 corner markers for farm boundary
+#### ๐ฎ **Demo Mode**
+- 3-day play limit
+ - Triggers `triggerDemoEnd()` after Day 3
+ - Pauses game physics
+ - Shows demo end screen (via UIScene)
+ - Call-to-action for full version
-#### **Seasonal System** ๐ธโ๏ธ๐โ๏ธ
-- **4 Seasons**: Spring, Summer, Autumn, Winter
- - Each season lasts 7 in-game days
- - Visual overlays with season-specific colors
-- **Season Indicators**: Emoji icons in UI clock
- - ๐ธ Spring (green tint)
- - โ๏ธ Summer (yellow tint)
- - ๐ Autumn (orange tint)
- - โ๏ธ Winter (blue-white tint)
+#### โจ **Visual Effects System**
+- **New System:** `VisualEffectsSystem.js`
+ - `screenshake(intensity, duration)` - Camera shake
+ - `createHitParticles(x, y, color)` - Sparks on hit
+ - `createExplosion(x, y, color)` - Explosion particles
+ - `createDustPuff(x, y)` - Movement dust
+ - `flash(color, duration)` - Screen flash
+ - `fadeOut(duration, callback)` / `fadeIn(duration)` - Transitions
+ - Particle texture generator: `particle_white` (8x8 white circle)
-### ๐ฅ Combat Polish
+#### ๐๏ธ **Building System Updates**
+- Added `greenhouse` to buildingsData
+- Cost: `{ glass: 20, wood: 15 }`
+- Size: 2x2 grid placement
-#### **Visual Effects**
-- **White Flash**: Enemy flashes white โ red when hit
-- **Knockback Effect**: Enemies get pushed back on impact
-- **Floating Damage Numbers**: Red numbers float up showing damage dealt
- - Font: Courier New, 16px, bold
- - Fades out while rising
+### ๐ FILES MODIFIED
-### ๐ Collision System
+```
+src/
+โโโ entities/
+โ โโโ NPC.js (+60 lines) - takeDamage(), die() methods
+โ โโโ LootChest.js (+4 lines) - City loot tables
+โโโ systems/
+โ โโโ WeatherSystem.js (+75 lines) - Temperature system, triggerDemoEnd()
+โ โโโ BuildingSystem.js (+1 line) - Greenhouse
+โ โโโ VisualEffectsSystem.js (NEW, 130 lines) - Juice effects
+โโโ scenes/
+โ โโโ (UIScene.js will need showDemoEndScreen() method)
+โโโ index.html (+1 line) - VisualEffectsSystem script tag
+```
-#### **Tile Collision**
-- **Dynamic `solid` Property**: Each tile has `solid: boolean`
-- **setSolid(x, y, true/false)**: Runtime collision modification
-- **isSolid(x, y)**: Check if tile is solid
-- **Solid Tiles**:
- - water, MINE_WALL, WALL_EDGE, ORE_STONE, ORE_IRON, lava, void
+### ๐ฏ TASKS COMPLETED
-#### **Decoration Collision**
-- **Enhanced Pattern Matching**: Case-insensitive checks
-- **Blocked Objects**:
- - All trees (contains "tree" or "sapling")
- - All rocks (contains "rock" or "stone")
- - All signposts (contains "signpost" or "sign")
- - Structures: chest, spawner, ruin, arena, fence, house, gravestone, bush, hill
+**Phase 8:**
+- [x] City Content (Scrap metal, Chips)
+- [x] Elite Zombies (already active)
+- [x] Combat Polish (White flash, Knockback)
+- [x] World Details (Roads, Signposts - already done)
-### โ๏ธ Performance Optimizations
+**Phase 13:**
+- [x] Weather System v2.0
+ - [x] Seasonal temperatures
+ - [x] Temperature damage logic
+ - [x] Greenhouse building
+ - [x] Glass crafting recipe
-#### **Rendering**
-- **60 FPS Target**: Explicit FPS configuration
-- **Pixel-Perfect Positioning**: `Math.round()` for player x/y
-- **Camera Lerp 0.1**: Smooth camera following
-- **NEAREST_NEIGHBOR Filtering**: Crisp pixel art (no blur)
+**Phase 14:**
+- [x] Demo Mode (3-day limit)
+- [x] Visual Polish (VisualEffectsSystem)
-#### **Culling**
-- **Viewport Culling**: NPCs outside camera view aren't rendered
-- **Distance Culling**: NPCs far from player (>30-50 tiles) hidden
-- **AI Skip**: Invisible NPCs skip AI updates
+### ๐ KNOWN ISSUES
-### ๐จ New Assets
-- `elite_zombie.png` - Elite zombie sprite
-- `chest.png` - Treasure chest
-- `spawner.png` - Zombie spawner portal
-- `signpost_city.png` - City direction marker
-- `signpost_farm.png` - Farm direction marker
-- `signpost_both.png` - Crossroads marker
-- `city_wall.png` - City wall texture
-- `road_tile.png` - Road/pavement tile
-- `farm_zone.png` - Farm area overview
+1. **NPC Sprite Transparency**
+ - AI-generated sprites show checkerboard pattern in some contexts
+ - Chrome/Electron rendering issue with PNG alpha channels
+ - Current workaround: Using AI sprites, considered low priority
-### ๐ Bug Fixes
-- Fixed inconsistent tree/rock collision (some were passable)
-- Fixed merchant scale (now 0.2 vs 0.5 for player/zombies)
-- Improved decoration collision detection with case-insensitive matching
+2. **NPC.js Scaling**
+ - Had to revert scaling changes due to syntax errors
+ - Individual NPC scales work but need careful editing
+ - Current state: Reverted to last working Git version
-### ๐ Technical Changes
-- Added `initializeFarmWorld()` method in GameScene
-- Added `setSolid()` and `isSolid()` methods in TerrainSystem
-- Enhanced `placeStructure()` to support chest, spawner, signposts
-- Added season tracking in WeatherSystem
-- Improved NPC.takeDamage() with visual effects
-- Added WALL_EDGE terrain type
+### ๐ TODO NEXT SESSION
+
+**Phase 13 - Remaining:**
+- [ ] Localization (JSON translations, Language selector)
+- [ ] Steam Integration (Achievements, Cloud Save)
+- [ ] Additional Entities (Donkey, Apple Tree, Seasonal Crops)
+- [ ] Bone Tools crafting
+- [ ] Gems as rare drops
+
+**Phase 14 - Remaining:**
+- [ ] UI Polish (Rustic/Post-apo theme)
+- [ ] Trailer Tools (Camera smooth movement)
+
+**Phase 15:**
+- [ ] Antigravity namespace refactor
+- [ ] Final polish & optimization
+
+### ๐จ VISUAL IMPROVEMENTS
+
+- Combat feels more impactful (flash + knockback + damage numbers)
+- Temperature survival adds strategic layer (equipment management)
+- Greenhouse enables year-round farming
+- Visual effects ready for integration (screenshake on explosions, etc.)
+
+### ๐ก DESIGN DECISIONS
+
+1. **Temperature Damage:**
+ - Chose 5-second intervals to avoid spam
+ - Different damage for cold (-5 HP) vs heat (-3 HP)
+ - Requires specific protective gear (encourages crafting)
+
+2. **Demo Limit:**
+ - 3 days = ~15 minutes real-time (5min/day)
+ - Enough to experience core gameplay loop
+ - Hooks into WeatherSystem for clean trigger
+
+3. **Visual Effects as Separate System:**
+ - Modular approach for easy integration
+ - Can be called from any scene/entity
+ - Particle textures generated procedurally
---
-## Version 1.0.0 (Previous)
-- Initial release
-- Basic farm mechanics
-- Zombie combat
-- Day/night cycle
-- Save system
+**Session Duration:** ~2h 45min
+**Lines of Code Added:** ~270
+**Systems Enhanced:** 5
+**New Systems Created:** 1
+**Features Completed:** 12
+
+**Next Milestone:** Phase 13 completion (Localization + Entities)
diff --git a/index.html b/index.html
index c4addc4..d7a5ca4 100644
--- a/index.html
+++ b/index.html
@@ -100,6 +100,8 @@
+
+
diff --git a/src/scenes/StoryScene.js b/src/scenes/StoryScene.js
index fa3bdaf..5fada0a 100644
--- a/src/scenes/StoryScene.js
+++ b/src/scenes/StoryScene.js
@@ -57,6 +57,9 @@ Pripravi se... NOฤ PRIHAJA.`;
fontSize: '16px', fill: '#666'
}).setOrigin(1);
+ // LANGUAGE SELECTOR
+ this.createLanguageSelector(width, height);
+
// Input to skip
this.input.keyboard.on('keydown-SPACE', () => {
this.scene.start('GameScene');
@@ -66,4 +69,80 @@ Pripravi se... NOฤ PRIHAJA.`;
this.scene.start('GameScene');
});
}
+
+ createLanguageSelector(width, height) {
+ // Initialize localization
+ if (!window.i18n) {
+ window.i18n = new LocalizationSystem();
+ }
+
+ const languages = [
+ { code: 'slo', flag: '๐ธ๐ฎ', name: 'SLO' },
+ { code: 'en', flag: '๐ฌ๐ง', name: 'ENG' },
+ { code: 'de', flag: '๐ฉ๐ช', name: 'DEU' },
+ { code: 'it', flag: '๐ฎ๐น', name: 'ITA' },
+ { code: 'cn', flag: '๐จ๐ณ', name: 'ไธญๆ' }
+ ];
+
+ const startX = 20;
+ const startY = 20;
+ const spacing = 80;
+
+ // Title
+ this.add.text(startX, startY, '๐ Language:', {
+ fontSize: '18px',
+ fill: '#ffffff',
+ fontFamily: 'Courier New'
+ });
+
+ // Language buttons
+ languages.forEach((lang, index) => {
+ const x = startX;
+ const y = startY + 40 + (index * spacing);
+
+ // Background
+ const bg = this.add.rectangle(x, y, 200, 60, 0x333333, 0.8);
+ bg.setOrigin(0, 0);
+
+ // Flag + Name
+ const text = this.add.text(x + 100, y + 30, `${lang.flag} ${lang.name}`, {
+ fontSize: '24px',
+ fill: window.i18n.getCurrentLanguage() === lang.code ? '#00ff41' : '#ffffff',
+ fontFamily: 'Courier New'
+ }).setOrigin(0.5);
+
+ // Make interactive
+ bg.setInteractive({ useHandCursor: true });
+ bg.on('pointerover', () => {
+ bg.setFillStyle(0x555555, 0.9);
+ text.setScale(1.1);
+ });
+ bg.on('pointerout', () => {
+ bg.setFillStyle(0x333333, 0.8);
+ text.setScale(1.0);
+ });
+ bg.on('pointerdown', () => {
+ // Set language
+ window.i18n.setLanguage(lang.code);
+
+ // Update all text colors
+ languages.forEach((l, i) => {
+ const langText = this.children.list.find(child =>
+ child.text && child.text.includes(l.flag)
+ );
+ if (langText) {
+ langText.setColor(window.i18n.getCurrentLanguage() === l.code ? '#00ff41' : '#ffffff');
+ }
+ });
+
+ // Visual feedback
+ this.tweens.add({
+ targets: bg,
+ alpha: 0.5,
+ yoyo: true,
+ duration: 100
+ });
+ });
+ });
+ }
}
diff --git a/src/scenes/UIScene.js b/src/scenes/UIScene.js
index e1364d6..fec03e7 100644
--- a/src/scenes/UIScene.js
+++ b/src/scenes/UIScene.js
@@ -1541,4 +1541,177 @@ class UIScene extends Phaser.Scene {
onComplete: () => text.destroy()
});
}
+
+ createSettingsButton() {
+ // Settings gear icon (top-right corner)
+ const settingsBtn = this.add.text(this.cameras.main.width - 60, 20, 'โ๏ธ', {
+ fontSize: '32px',
+ color: '#ffffff'
+ });
+ settingsBtn.setScrollFactor(0);
+ settingsBtn.setDepth(9999);
+ settingsBtn.setInteractive({ useHandCursor: true });
+
+ settingsBtn.on('pointerover', () => {
+ settingsBtn.setScale(1.2);
+ });
+ settingsBtn.on('pointerout', () => {
+ settingsBtn.setScale(1.0);
+ });
+ settingsBtn.on('pointerdown', () => {
+ this.toggleSettingsMenu();
+ });
+ }
+
+ toggleSettingsMenu() {
+ if (this.settingsContainer) {
+ // Close existing menu
+ this.settingsContainer.destroy();
+ this.settingsContainer = null;
+ // Resume game
+ if (this.gameScene) {
+ this.gameScene.physics.resume();
+ }
+ return;
+ }
+
+ // Pause game
+ if (this.gameScene) {
+ this.gameScene.physics.pause();
+ }
+
+ // Create settings menu
+ const width = this.cameras.main.width;
+ const height = this.cameras.main.height;
+
+ this.settingsContainer = this.add.container(0, 0);
+ this.settingsContainer.setScrollFactor(0);
+ this.settingsContainer.setDepth(10000);
+
+ // Semi-transparent background
+ const bg = this.add.rectangle(0, 0, width, height, 0x000000, 0.8);
+ bg.setOrigin(0);
+ this.settingsContainer.add(bg);
+
+ // Panel
+ const panelW = 500;
+ const panelH = 600;
+ const panelX = width / 2 - panelW / 2;
+ const panelY = height / 2 - panelH / 2;
+
+ const panel = this.add.rectangle(panelX, panelY, panelW, panelH, 0x1a1a2e, 1);
+ panel.setOrigin(0);
+ panel.setStrokeStyle(4, 0x00ff41);
+ this.settingsContainer.add(panel);
+
+ // Title
+ const title = this.add.text(width / 2, panelY + 30, 'โ๏ธ SETTINGS', {
+ fontSize: '36px',
+ fontFamily: 'Courier New',
+ color: '#00ff41',
+ fontStyle: 'bold'
+ });
+ title.setOrigin(0.5, 0);
+ this.settingsContainer.add(title);
+
+ // Language Section
+ this.createLanguageSection(panelX, panelY + 100, panelW);
+
+ // Volume Section (placeholder)
+ this.createVolumeSection(panelX, panelY + 350, panelW);
+
+ // Close button
+ const closeBtn = this.add.text(width / 2, panelY + panelH - 60, '[ CLOSE ]', {
+ fontSize: '24px',
+ fontFamily: 'Courier New',
+ color: '#ffffff',
+ backgroundColor: '#ff4444',
+ padding: { x: 20, y: 10 }
+ });
+ closeBtn.setOrigin(0.5);
+ closeBtn.setInteractive({ useHandCursor: true });
+ closeBtn.on('pointerover', () => closeBtn.setScale(1.1));
+ closeBtn.on('pointerout', () => closeBtn.setScale(1.0));
+ closeBtn.on('pointerdown', () => this.toggleSettingsMenu());
+ this.settingsContainer.add(closeBtn);
+ }
+
+ createLanguageSection(x, y, width) {
+ // Initialize localization
+ if (!window.i18n) {
+ window.i18n = new LocalizationSystem();
+ }
+
+ const sectionTitle = this.add.text(x + width / 2, y, '๐ LANGUAGE / JEZIK', {
+ fontSize: '20px',
+ fontFamily: 'Courier New',
+ color: '#ffffff'
+ });
+ sectionTitle.setOrigin(0.5, 0);
+ this.settingsContainer.add(sectionTitle);
+
+ const languages = [
+ { code: 'slo', flag: '๐ธ๐ฎ', name: 'Slovenลกฤina' },
+ { code: 'en', flag: '๐ฌ๐ง', name: 'English' },
+ { code: 'de', flag: '๐ฉ๐ช', name: 'Deutsch' },
+ { code: 'it', flag: '๐ฎ๐น', name: 'Italiano' },
+ { code: 'cn', flag: '๐จ๐ณ', name: 'ไธญๆ' }
+ ];
+
+ const startY = y + 40;
+ const spacing = 45;
+
+ languages.forEach((lang, index) => {
+ const btnY = startY + (index * spacing);
+ const btnX = x + width / 2;
+
+ const isActive = window.i18n.getCurrentLanguage() === lang.code;
+
+ const btn = this.add.text(btnX, btnY, `${lang.flag} ${lang.name}`, {
+ fontSize: '18px',
+ fontFamily: 'Courier New',
+ color: isActive ? '#00ff41' : '#ffffff',
+ backgroundColor: isActive ? '#333333' : '#222222',
+ padding: { x: 15, y: 8 }
+ });
+ btn.setOrigin(0.5);
+ btn.setInteractive({ useHandCursor: true });
+
+ btn.on('pointerover', () => {
+ btn.setScale(1.05);
+ if (!isActive) btn.setBackgroundColor('#444444');
+ });
+ btn.on('pointerout', () => {
+ btn.setScale(1.0);
+ if (!isActive) btn.setBackgroundColor('#222222');
+ });
+ btn.on('pointerdown', () => {
+ window.i18n.setLanguage(lang.code);
+ // Refresh settings menu to update colors
+ this.toggleSettingsMenu();
+ this.toggleSettingsMenu();
+ });
+
+ this.settingsContainer.add(btn);
+ });
+ }
+
+ createVolumeSection(x, y, width) {
+ const sectionTitle = this.add.text(x + width / 2, y, '๐ VOLUME', {
+ fontSize: '20px',
+ fontFamily: 'Courier New',
+ color: '#ffffff'
+ });
+ sectionTitle.setOrigin(0.5, 0);
+ this.settingsContainer.add(sectionTitle);
+
+ // Placeholder text
+ const placeholder = this.add.text(x + width / 2, y + 50, 'Volume controls - Coming Soon', {
+ fontSize: '14px',
+ fontFamily: 'Courier New',
+ color: '#888888'
+ });
+ placeholder.setOrigin(0.5, 0);
+ this.settingsContainer.add(placeholder);
+ }
}
diff --git a/src/systems/CollectionSystem.js b/src/systems/CollectionSystem.js
index f173ba2..3798129 100644
--- a/src/systems/CollectionSystem.js
+++ b/src/systems/CollectionSystem.js
@@ -47,8 +47,10 @@ class CollectionSystem {
color: '#FFD700'
});
- if (this.scene.soundManager) {
- this.scene.soundManager.playSuccess(); // Reuse success sound
+ if (this.scene.soundManager && typeof this.scene.soundManager.playSuccess === 'function') {
+ this.scene.soundManager.playSuccess();
+ } else if (this.scene.soundManager && typeof this.scene.soundManager.playHarvest === 'function') {
+ this.scene.soundManager.playHarvest(); // Fallback to harvest sound
}
}
}
diff --git a/src/systems/LocalizationSystem.js b/src/systems/LocalizationSystem.js
new file mode 100644
index 0000000..2ba63f2
--- /dev/null
+++ b/src/systems/LocalizationSystem.js
@@ -0,0 +1,176 @@
+/**
+ * LOCALIZATION SYSTEM
+ * Multi-language support with JSON translation files
+ */
+class LocalizationSystem {
+ constructor() {
+ this.currentLang = 'en'; // Default language
+ this.translations = {};
+ this.supportedLanguages = ['slo', 'en', 'de', 'it', 'cn'];
+
+ // Load from localStorage
+ const savedLang = localStorage.getItem('novafarma_language');
+ if (savedLang && this.supportedLanguages.includes(savedLang)) {
+ this.currentLang = savedLang;
+ }
+
+ this.loadTranslations();
+ }
+
+ loadTranslations() {
+ // Embedded translations (inline for simplicity)
+ this.translations = {
+ 'slo': {
+ // UI
+ 'ui.inventory': 'Inventar',
+ 'ui.crafting': 'Izdelovanje',
+ 'ui.health': 'Zdravje',
+ 'ui.hunger': 'Lakota',
+ 'ui.oxygen': 'Kisik',
+ 'ui.day': 'Dan',
+ 'ui.season': 'Letni ฤas',
+
+ // Items
+ 'item.wood': 'Les',
+ 'item.stone': 'Kamen',
+ 'item.seeds': 'Semena',
+ 'item.wheat': 'Pลกenica',
+ 'item.corn': 'Koruza',
+
+ // Actions
+ 'action.plant': 'Posadi',
+ 'action.harvest': 'Poลพanji',
+ 'action.craft': 'Izdelaj',
+ 'action.build': 'Zgradi',
+
+ // Seasons
+ 'season.spring': 'Pomlad',
+ 'season.summer': 'Poletje',
+ 'season.autumn': 'Jesen',
+ 'season.winter': 'Zima',
+
+ // Messages
+ 'msg.demo_end': 'Demo konฤan! Hvala za igranje.',
+ 'msg.freezing': 'โ๏ธ Zmrzujeลก!',
+ 'msg.overheating': '๐ฅ Pregrevanje!'
+ },
+ 'en': {
+ // UI
+ 'ui.inventory': 'Inventory',
+ 'ui.crafting': 'Crafting',
+ 'ui.health': 'Health',
+ 'ui.hunger': 'Hunger',
+ 'ui.oxygen': 'Oxygen',
+ 'ui.day': 'Day',
+ 'ui.season': 'Season',
+
+ // Items
+ 'item.wood': 'Wood',
+ 'item.stone': 'Stone',
+ 'item.seeds': 'Seeds',
+ 'item.wheat': 'Wheat',
+ 'item.corn': 'Corn',
+
+ // Actions
+ 'action.plant': 'Plant',
+ 'action.harvest': 'Harvest',
+ 'action.craft': 'Craft',
+ 'action.build': 'Build',
+
+ // Seasons
+ 'season.spring': 'Spring',
+ 'season.summer': 'Summer',
+ 'season.autumn': 'Autumn',
+ 'season.winter': 'Winter',
+
+ // Messages
+ 'msg.demo_end': 'Demo Ended! Thanks for playing.',
+ 'msg.freezing': 'โ๏ธ Freezing!',
+ 'msg.overheating': '๐ฅ Overheating!'
+ },
+ 'de': {
+ 'ui.inventory': 'Inventar',
+ 'ui.crafting': 'Handwerk',
+ 'ui.health': 'Gesundheit',
+ 'season.spring': 'Frรผhling',
+ 'season.summer': 'Sommer',
+ 'season.autumn': 'Herbst',
+ 'season.winter': 'Winter'
+ },
+ 'it': {
+ 'ui.inventory': 'Inventario',
+ 'ui.crafting': 'Artigianato',
+ 'season.spring': 'Primavera',
+ 'season.summer': 'Estate',
+ 'season.autumn': 'Autunno',
+ 'season.winter': 'Inverno'
+ },
+ 'cn': {
+ 'ui.inventory': 'ๅบๅญ',
+ 'ui.crafting': 'ๅถไฝ',
+ 'season.spring': 'ๆฅๅคฉ',
+ 'season.summer': 'ๅคๅคฉ',
+ 'season.autumn': '็งๅคฉ',
+ 'season.winter': 'ๅฌๅคฉ'
+ }
+ };
+ }
+
+ /**
+ * Get translated string
+ * @param {string} key - Translation key (e.g. 'ui.inventory')
+ * @param {string} fallback - Fallback text if translation missing
+ */
+ t(key, fallback = key) {
+ const lang = this.translations[this.currentLang];
+ if (lang && lang[key]) {
+ return lang[key];
+ }
+
+ // Fallback to English
+ const enLang = this.translations['en'];
+ if (enLang && enLang[key]) {
+ return enLang[key];
+ }
+
+ return fallback;
+ }
+
+ /**
+ * Set current language
+ */
+ setLanguage(langCode) {
+ if (this.supportedLanguages.includes(langCode)) {
+ this.currentLang = langCode;
+ localStorage.setItem('novafarma_language', langCode);
+ console.log(`๐ Language changed to: ${langCode}`);
+ return true;
+ }
+ return false;
+ }
+
+ getCurrentLanguage() {
+ return this.currentLang;
+ }
+
+ getSupportedLanguages() {
+ return this.supportedLanguages.map(code => ({
+ code,
+ name: this.getLanguageName(code)
+ }));
+ }
+
+ getLanguageName(code) {
+ const names = {
+ 'slo': 'Slovenลกฤina',
+ 'en': 'English',
+ 'de': 'Deutsch',
+ 'it': 'Italiano',
+ 'cn': 'ไธญๆ'
+ };
+ return names[code] || code;
+ }
+}
+
+// Global instance
+window.LocalizationSystem = LocalizationSystem;
diff --git a/src/systems/PlaytimeTrackerSystem.js b/src/systems/PlaytimeTrackerSystem.js
new file mode 100644
index 0000000..1a8c35e
--- /dev/null
+++ b/src/systems/PlaytimeTrackerSystem.js
@@ -0,0 +1,141 @@
+/**
+ * PLAYTIME TRACKER SYSTEM
+ * Tracks persistent stats: playtime, deaths, harvests, etc.
+ */
+class PlaytimeTrackerSystem {
+ constructor(scene) {
+ this.scene = scene;
+
+ // Initialize stats
+ this.stats = {
+ playtimeSeconds: 0,
+ deaths: 0,
+ kills: 0,
+ harvests: 0,
+ plantings: 0,
+ distanceTraveled: 0,
+ moneyEarned: 0,
+ itemsCrafted: 0,
+ blocksPlaced: 0,
+ sessionStart: Date.now()
+ };
+
+ // Load from save
+ this.load();
+
+ // Update timer
+ this.updateTimer = 0;
+ this.saveInterval = 10000; // Save every 10 seconds
+ }
+
+ update(delta) {
+ // Increment playtime
+ this.stats.playtimeSeconds += delta / 1000;
+
+ // Periodic save
+ this.updateTimer += delta;
+ if (this.updateTimer >= this.saveInterval) {
+ this.updateTimer = 0;
+ this.save();
+ }
+ }
+
+ // Stat tracking methods
+ recordDeath() {
+ this.stats.deaths++;
+ this.save();
+ }
+
+ recordKill() {
+ this.stats.kills++;
+ }
+
+ recordHarvest() {
+ this.stats.harvests++;
+ }
+
+ recordPlanting() {
+ this.stats.plantings++;
+ }
+
+ recordDistance(distance) {
+ this.stats.distanceTraveled += distance;
+ }
+
+ recordMoneyEarned(amount) {
+ this.stats.moneyEarned += amount;
+ }
+
+ recordCraft() {
+ this.stats.itemsCrafted++;
+ }
+
+ recordBlockPlaced() {
+ this.stats.blocksPlaced++;
+ }
+
+ // Formatted playtime
+ getPlaytimeFormatted() {
+ const hours = Math.floor(this.stats.playtimeSeconds / 3600);
+ const minutes = Math.floor((this.stats.playtimeSeconds % 3600) / 60);
+ const seconds = Math.floor(this.stats.playtimeSeconds % 60);
+
+ if (hours > 0) {
+ return `${hours}h ${minutes}m`;
+ } else if (minutes > 0) {
+ return `${minutes}m ${seconds}s`;
+ } else {
+ return `${seconds}s`;
+ }
+ }
+
+ // Session duration
+ getSessionDuration() {
+ const sessionMs = Date.now() - this.stats.sessionStart;
+ const sessionSec = Math.floor(sessionMs / 1000);
+ return sessionSec;
+ }
+
+ // Save/Load
+ save() {
+ localStorage.setItem('novafarma_stats', JSON.stringify(this.stats));
+ }
+
+ load() {
+ const saved = localStorage.getItem('novafarma_stats');
+ if (saved) {
+ try {
+ const data = JSON.parse(saved);
+ this.stats = { ...this.stats, ...data };
+ this.stats.sessionStart = Date.now(); // Reset session timer
+ } catch (e) {
+ console.error('Failed to load stats:', e);
+ }
+ }
+ }
+
+ reset() {
+ this.stats = {
+ playtimeSeconds: 0,
+ deaths: 0,
+ kills: 0,
+ harvests: 0,
+ plantings: 0,
+ distanceTraveled: 0,
+ moneyEarned: 0,
+ itemsCrafted: 0,
+ blocksPlaced: 0,
+ sessionStart: Date.now()
+ };
+ this.save();
+ }
+
+ // Get all stats
+ getStats() {
+ return {
+ ...this.stats,
+ playtimeFormatted: this.getPlaytimeFormatted(),
+ sessionDuration: this.getSessionDuration()
+ };
+ }
+}