test
This commit is contained in:
249
docs/CHARACTER_SETUP_GUIDE.md
Normal file
249
docs/CHARACTER_SETUP_GUIDE.md
Normal file
@@ -0,0 +1,249 @@
|
||||
# 🎮 KRVAVA ŽETEV - Character Setup Guide
|
||||
|
||||
Vodič za ustvarjanje in dodajanje novih karakterjev v igro.
|
||||
|
||||
---
|
||||
|
||||
## 📋 Trenutne Nastavitve Igralca
|
||||
|
||||
### Player Character (Protagonist)
|
||||
- **Sprite**: `assets/sprites/player_walking_clean.png`
|
||||
- **Texture Key**: `player_protagonist`
|
||||
- **Frame Size**: 256x256 pixels
|
||||
- **Total Size**: 1024x1024 pixels (4x4 grid = 16 frames)
|
||||
- **Scale**: 0.5 (prikazuje kot 128x128)
|
||||
- **Origin**: (0.5, 1.0) - bottom center
|
||||
|
||||
### Animation Layout
|
||||
```
|
||||
Row 1 (frames 0-3): DOWN (spredaj, proti kameri)
|
||||
Row 2 (frames 4-7): LEFT (levo, stranski pogled)
|
||||
Row 3 (frames 8-11): RIGHT (desno, stranski pogled)
|
||||
Row 4 (frames 12-15): UP (zadaj, hrbtni pogled)
|
||||
```
|
||||
|
||||
### Animacije
|
||||
- `protagonist_walk_down` - frames 0-3 @ 8 fps
|
||||
- `protagonist_walk_left` - frames 4-7 @ 8 fps
|
||||
- `protagonist_walk_right` - frames 8-11 @ 8 fps
|
||||
- `protagonist_walk_up` - frames 12-15 @ 8 fps
|
||||
- `protagonist_idle_down` - frame 0
|
||||
- `protagonist_idle_left` - frame 4
|
||||
- `protagonist_idle_right` - frame 8
|
||||
- `protagonist_idle_up` - frame 12
|
||||
|
||||
---
|
||||
|
||||
## 🎨 Kako Ustvariti Nov Karakter
|
||||
|
||||
### 1. Pripravi Spritesheet Sliko
|
||||
|
||||
**Zahteve:**
|
||||
- **Format**: PNG s transparentnim ozadjem
|
||||
- **Velikost**: 1024x1024 pixels (ali 512x512 ali 2048x2048 - mora biti deljivo s 4)
|
||||
- **Grid**: 4x4 (16 frames)
|
||||
- **Frame Size**: 256x256 pixels (ali 128x128, 512x512)
|
||||
- **Ozadje**: 100% transparentno (brez šahovnice!)
|
||||
|
||||
**Če imaš sliko s šahovnico**, uporabi Python script:
|
||||
```bash
|
||||
python tools/remove_checkerboard.py
|
||||
```
|
||||
|
||||
### 2. Shrani Sliko v Pravilno Mapo
|
||||
|
||||
```
|
||||
c:\novafarma\assets\sprites\[character_name]_walking.png
|
||||
```
|
||||
|
||||
Primer:
|
||||
- `zombie_walking.png`
|
||||
- `merchant_walking.png`
|
||||
- `knight_walking.png`
|
||||
|
||||
### 3. Dodaj v PreloadScene.js
|
||||
|
||||
Odpri: `c:\novafarma\src\scenes\PreloadScene.js`
|
||||
|
||||
**V preload() funkciji dodaj:**
|
||||
```javascript
|
||||
// Nov Karakter - [IME]
|
||||
this.load.spritesheet('[character_key]', 'assets/sprites/[character_name]_walking.png', {
|
||||
frameWidth: 256, // Nastavi glede na velikost frame-a
|
||||
frameHeight: 256
|
||||
});
|
||||
```
|
||||
|
||||
**V createAnimations() funkciji dodaj:**
|
||||
```javascript
|
||||
// [CHARACTER_NAME] Walking Animations
|
||||
this.anims.create({
|
||||
key: '[character]_walk_down',
|
||||
frames: this.anims.generateFrameNumbers('[character_key]', { start: 0, end: 3 }),
|
||||
frameRate: 8,
|
||||
repeat: -1
|
||||
});
|
||||
|
||||
this.anims.create({
|
||||
key: '[character]_walk_left',
|
||||
frames: this.anims.generateFrameNumbers('[character_key]', { start: 4, end: 7 }),
|
||||
frameRate: 8,
|
||||
repeat: -1
|
||||
});
|
||||
|
||||
this.anims.create({
|
||||
key: '[character]_walk_right',
|
||||
frames: this.anims.generateFrameNumbers('[character_key]', { start: 8, end: 11 }),
|
||||
frameRate: 8,
|
||||
repeat: -1
|
||||
});
|
||||
|
||||
this.anims.create({
|
||||
key: '[character]_walk_up',
|
||||
frames: this.anims.generateFrameNumbers('[character_key]', { start: 12, end: 15 }),
|
||||
frameRate: 8,
|
||||
repeat: -1
|
||||
});
|
||||
|
||||
// Idle animations
|
||||
this.anims.create({
|
||||
key: '[character]_idle_down',
|
||||
frames: [{ key: '[character_key]', frame: 0 }],
|
||||
frameRate: 1
|
||||
});
|
||||
|
||||
this.anims.create({
|
||||
key: '[character]_idle_left',
|
||||
frames: [{ key: '[character_key]', frame: 4 }],
|
||||
frameRate: 1
|
||||
});
|
||||
|
||||
this.anims.create({
|
||||
key: '[character]_idle_right',
|
||||
frames: [{ key: '[character_key]', frame: 8 }],
|
||||
frameRate: 1
|
||||
});
|
||||
|
||||
this.anims.create({
|
||||
key: '[character]_idle_up',
|
||||
frames: [{ key: '[character_key]', frame: 12 }],
|
||||
frameRate: 1
|
||||
});
|
||||
```
|
||||
|
||||
### 4. Ustvari Character Class
|
||||
|
||||
Ustvari novo datoteko: `c:\novafarma\src\entities\[CharacterName].js`
|
||||
|
||||
**Template:**
|
||||
```javascript
|
||||
// [Character Name] Entity
|
||||
class [ClassName] {
|
||||
constructor(scene, gridX, gridY, offsetX = 0, offsetY = 0) {
|
||||
this.scene = scene;
|
||||
this.gridX = gridX;
|
||||
this.gridY = gridY;
|
||||
this.offsetX = offsetX;
|
||||
this.offsetY = offsetY;
|
||||
|
||||
this.iso = new IsometricUtils(48, 24);
|
||||
this.direction = 'down';
|
||||
this.isMoving = false;
|
||||
|
||||
this.createSprite();
|
||||
}
|
||||
|
||||
createSprite() {
|
||||
const screenPos = this.iso.toScreen(this.gridX, this.gridY);
|
||||
this.sprite = this.scene.add.sprite(
|
||||
screenPos.x + this.offsetX,
|
||||
screenPos.y + this.offsetY,
|
||||
'[character_key]',
|
||||
0
|
||||
);
|
||||
this.sprite.setOrigin(0.5, 1.0);
|
||||
this.sprite.setScale(0.5); // Nastavi glede na velikost
|
||||
|
||||
// Play idle animation
|
||||
if (this.scene.anims.exists('[character]_idle_down')) {
|
||||
this.sprite.play('[character]_idle_down');
|
||||
}
|
||||
}
|
||||
|
||||
update(delta) {
|
||||
// Add character logic here
|
||||
}
|
||||
|
||||
destroy() {
|
||||
if (this.sprite) this.sprite.destroy();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 5. Dodaj v GameScene.js
|
||||
|
||||
```javascript
|
||||
// Import character
|
||||
// (če uporabljaš module system)
|
||||
|
||||
// V create() funkciji:
|
||||
this.myCharacter = new [ClassName](this, 50, 50, this.terrainSystem.offsetX, this.terrainSystem.offsetY);
|
||||
|
||||
// V update() funkciji:
|
||||
this.myCharacter.update(delta);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🛠️ Python Script za Odstranitev Šahovnice
|
||||
|
||||
**Lokacija:** `c:\novafarma\tools\remove_checkerboard.py`
|
||||
|
||||
**Uporaba:**
|
||||
1. Odpri `remove_checkerboard.py`
|
||||
2. Spremeni `input_file` in `output_file` pot
|
||||
3. Zaženi: `python tools\remove_checkerboard.py`
|
||||
|
||||
**Primer:**
|
||||
```python
|
||||
input_file = r"C:\Users\Downloads\my_character.png"
|
||||
output_file = r"c:\novafarma\assets\sprites\my_character_clean.png"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ✅ Checklist za Nov Karakter
|
||||
|
||||
- [ ] Ustvari/pridobi spritesheet (1024x1024, 4x4 grid, transparentno ozadje)
|
||||
- [ ] Odstrani šahovnico (če je potrebno)
|
||||
- [ ] Shrani v `assets/sprites/`
|
||||
- [ ] Dodaj spritesheet load v `PreloadScene.js`
|
||||
- [ ] Dodaj animacije v `PreloadScene.js`
|
||||
- [ ] Ustvari Character class (opcijsko)
|
||||
- [ ] Dodaj v `GameScene.js` (opcijsko)
|
||||
- [ ] Testiraj v igri (osveži s Ctrl+R)
|
||||
|
||||
---
|
||||
|
||||
## 📐 Velikosti & Skale
|
||||
|
||||
| Frame Size | Total Size | Priporočen Scale | Display Size |
|
||||
|------------|------------|------------------|--------------|
|
||||
| 128x128 | 512x512 | 1.0 | 128x128 |
|
||||
| 256x256 | 1024x1024 | 0.5 | 128x128 |
|
||||
| 512x512 | 2048x2048 | 0.25 | 128x128 |
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Priporočila
|
||||
|
||||
1. **Consistent Size**: Vsi karakterji naj imajo enako display velikost (128x128)
|
||||
2. **Transparent Background**: Vedno uporabi PNG s 100% transparentnim ozadjem
|
||||
3. **Frame Rate**: 8 fps je idealno za walking animacije
|
||||
4. **Origin Point**: (0.5, 1.0) za bottom-center je najboljše za isometric igre
|
||||
5. **Backup**: Vedno shrani original pred odstranjevanjem šahovnice
|
||||
|
||||
---
|
||||
|
||||
**Zadnja posodobitev:** 14.12.2025
|
||||
**Avtor:** KRVAVA ŽETEV Team
|
||||
489
docs/CRAFTING_GUIDE.md
Normal file
489
docs/CRAFTING_GUIDE.md
Normal file
@@ -0,0 +1,489 @@
|
||||
# 🛠️ Crafting System Guide
|
||||
|
||||
Kompletni vodič za dodajanje in ustvarjanje crafting receptov v KRVAVA ŽETEV igri.
|
||||
|
||||
---
|
||||
|
||||
## 📖 Pregled
|
||||
|
||||
Igra ima **crafting sistem** ki omogoča igralcu izdelovanje:
|
||||
- **Orodja** (sekira, kramp, motika)
|
||||
- **Orožje** (meč, lok)
|
||||
- **Zgradbe** (ograja, chest, furnace)
|
||||
- **Materiale** (palica, bakla)
|
||||
|
||||
---
|
||||
|
||||
## 📂 Lokacija Datotek
|
||||
|
||||
```
|
||||
c:\novafarma\src\data\CraftingRecipes.js ← Vsi recepti
|
||||
c:\novafarma\src\systems\CraftingTiersSystem.js ← Tier sistem
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎯 DVA FORMATA RECEPTOV
|
||||
|
||||
### **Format 1: Obstoječi (Podroben)** ✅ PRIPOROČENO
|
||||
|
||||
```javascript
|
||||
const CRAFTING_RECIPES = {
|
||||
'lesena_ograda': {
|
||||
id: 'lesena_ograda',
|
||||
name: 'Lesena Ograda',
|
||||
ingredients: [
|
||||
{ item: 'wood', amount: 2 }
|
||||
],
|
||||
result: { item: 'lesena_ograda', amount: 5 },
|
||||
category: 'buildings',
|
||||
description: 'Lesena ograja za zaščito farme.'
|
||||
},
|
||||
'sekira_osnovna': {
|
||||
id: 'sekira_osnovna',
|
||||
name: 'Osnovna Sekira',
|
||||
ingredients: [
|
||||
{ item: 'wood', amount: 3 },
|
||||
{ item: 'stone', amount: 2 }
|
||||
],
|
||||
result: { item: 'sekira_osnovna', amount: 1 },
|
||||
category: 'tools',
|
||||
description: 'Osnovno orodje za sekanje dreves.'
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
**Prednosti:**
|
||||
- ✅ Podpira kategorije (tools, weapons, buildings...)
|
||||
- ✅ Podpira opise
|
||||
- ✅ Združljivo z obstoječim sistemom
|
||||
- ✅ Enostavno razširljivo
|
||||
|
||||
---
|
||||
|
||||
### **Format 2: Enostaven/Kompakten**
|
||||
|
||||
```javascript
|
||||
const craftingRecipes = {
|
||||
'lesena_ograda': {
|
||||
requires: { 'wood': 2 },
|
||||
yields: 5
|
||||
},
|
||||
'sekira_osnovna': {
|
||||
requires: {
|
||||
'wood': 3,
|
||||
'stone': 2
|
||||
},
|
||||
yields: 1
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
**Prednosti:**
|
||||
- ✅ Krajši
|
||||
- ✅ Hitrejši za pisanje
|
||||
|
||||
**Slabosti:**
|
||||
- ❌ Ni kategorij
|
||||
- ❌ Ni opisov
|
||||
- ❌ Potreben converter
|
||||
|
||||
---
|
||||
|
||||
## 🔧 Converter Funkcija
|
||||
|
||||
Če želiš uporabljati enostavni format, uporabi converter:
|
||||
|
||||
```javascript
|
||||
/**
|
||||
* Pretvori enostavni format v obstoječi format
|
||||
* @param {Object} simpleRecipes - Enostavni format receptov
|
||||
* @returns {Object} - Obstoječi format receptov
|
||||
*/
|
||||
function convertToStandardFormat(simpleRecipes) {
|
||||
const standardRecipes = {};
|
||||
|
||||
for (const [id, recipe] of Object.entries(simpleRecipes)) {
|
||||
// Pretvori requires v ingredients array
|
||||
const ingredients = [];
|
||||
for (const [item, amount] of Object.entries(recipe.requires)) {
|
||||
ingredients.push({ item, amount });
|
||||
}
|
||||
|
||||
// Ustvari standardni format
|
||||
standardRecipes[id] = {
|
||||
id: id,
|
||||
name: id.replace(/_/g, ' ').replace(/\b\w/g, l => l.toUpperCase()),
|
||||
ingredients: ingredients,
|
||||
result: { item: id, amount: recipe.yields },
|
||||
category: 'custom',
|
||||
description: `Crafted item: ${id}`
|
||||
};
|
||||
}
|
||||
|
||||
return standardRecipes;
|
||||
}
|
||||
|
||||
// UPORABA:
|
||||
const enostavniRecepti = {
|
||||
'lesena_ograda': {
|
||||
requires: { 'wood': 2 },
|
||||
yields: 5
|
||||
}
|
||||
};
|
||||
|
||||
const standardRecepti = convertToStandardFormat(enostavniRecepti);
|
||||
// Rezultat:
|
||||
// {
|
||||
// 'lesena_ograda': {
|
||||
// id: 'lesena_ograda',
|
||||
// name: 'Lesena Ograda',
|
||||
// ingredients: [{ item: 'wood', amount: 2 }],
|
||||
// result: { item: 'lesena_ograda', amount: 5 },
|
||||
// category: 'custom',
|
||||
// description: 'Crafted item: lesena_ograda'
|
||||
// }
|
||||
// }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ✨ Kako Dodati Nov Recept
|
||||
|
||||
### **Korak 1: Odpri CraftingRecipes.js**
|
||||
|
||||
```
|
||||
c:\novafarma\src\data\CraftingRecipes.js
|
||||
```
|
||||
|
||||
### **Korak 2: Dodaj Nov Recept**
|
||||
|
||||
Najdi ustrezno kategorijo in dodaj:
|
||||
|
||||
```javascript
|
||||
const CRAFTING_RECIPES = {
|
||||
// ... obstoječi recepti ...
|
||||
|
||||
// ==================================
|
||||
// CUSTOM RECIPES (Novi!)
|
||||
// ==================================
|
||||
'lesena_ograda': {
|
||||
id: 'lesena_ograda',
|
||||
name: 'Lesena Ograda',
|
||||
ingredients: [
|
||||
{ item: 'wood', amount: 2 }
|
||||
],
|
||||
result: { item: 'lesena_ograda', amount: 5 },
|
||||
category: 'buildings',
|
||||
description: 'Lesena ograja za zaščito farme.'
|
||||
},
|
||||
|
||||
'sekira_osnovna': {
|
||||
id: 'sekira_osnovna',
|
||||
name: 'Osnovna Sekira',
|
||||
ingredients: [
|
||||
{ item: 'wood', amount: 3 },
|
||||
{ item: 'stone', amount: 2 }
|
||||
],
|
||||
result: { item: 'sekira_osnovna', amount: 1 },
|
||||
category: 'tools',
|
||||
description: 'Osnovno orodje za sekanje dreves.'
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### **Korak 3: Shrani in Osveži**
|
||||
|
||||
- Shrani datoteko
|
||||
- Osveži igro (Ctrl+R)
|
||||
- Odpri crafting menu (C tipka)
|
||||
|
||||
---
|
||||
|
||||
## 📋 Kategorije Receptov
|
||||
|
||||
| Kategorija | Opis | Primeri |
|
||||
|------------|------|---------|
|
||||
| `tools` | Orodja | Sekira, kramp, motika |
|
||||
| `weapons` | Orožje | Meč, lok, kopje |
|
||||
| `buildings` | Zgradbe | Ograja, chest, furnace |
|
||||
| `materials` | Materiali | Palica, deska, bakla |
|
||||
| `storage` | Shranjevanje | Chest, barrel |
|
||||
| `workstation` | Delavnice | Furnace, anvil |
|
||||
| `lighting` | Svetloba | Bakla, svetilka |
|
||||
| `custom` | Po meri | Tvoji recepti |
|
||||
|
||||
---
|
||||
|
||||
## 🎮 Helper Funkcije
|
||||
|
||||
### **getCraftingRecipe(id)**
|
||||
```javascript
|
||||
const recipe = getCraftingRecipe('lesena_ograda');
|
||||
console.log(recipe.name); // "Lesena Ograda"
|
||||
console.log(recipe.ingredients); // [{ item: 'wood', amount: 2 }]
|
||||
```
|
||||
|
||||
### **canCraft(id, inventory)**
|
||||
```javascript
|
||||
const canMake = canCraft('lesena_ograda', playerInventory);
|
||||
if (canMake) {
|
||||
console.log('✅ Lahko narediš ograjo!');
|
||||
} else {
|
||||
console.log('❌ Manjka material!');
|
||||
}
|
||||
```
|
||||
|
||||
### **craftItem(id, inventory)**
|
||||
```javascript
|
||||
const success = craftItem('lesena_ograda', playerInventory);
|
||||
if (success) {
|
||||
console.log('🔨 Izdelano!');
|
||||
// Inventory: wood -= 2, lesena_ograda += 5
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 💡 Praktični Primeri
|
||||
|
||||
### **Primer 1: Enostavna Palica**
|
||||
|
||||
```javascript
|
||||
'stick': {
|
||||
id: 'stick',
|
||||
name: 'Palica',
|
||||
ingredients: [
|
||||
{ item: 'wood', amount: 1 }
|
||||
],
|
||||
result: { item: 'stick', amount: 4 },
|
||||
category: 'materials',
|
||||
description: 'Osnoven material za crafting.'
|
||||
}
|
||||
```
|
||||
|
||||
### **Primer 2: Sekira (Več Ingredientov)**
|
||||
|
||||
```javascript
|
||||
'stone_axe': {
|
||||
id: 'stone_axe',
|
||||
name: 'Kamnita Sekira',
|
||||
ingredients: [
|
||||
{ item: 'stone', amount: 3 },
|
||||
{ item: 'stick', amount: 2 }
|
||||
],
|
||||
result: { item: 'stone_axe', amount: 1 },
|
||||
category: 'tools',
|
||||
description: 'Izboljšana sekira za hitrejše sekanje.'
|
||||
}
|
||||
```
|
||||
|
||||
### **Primer 3: Furnace (Kompleksno)**
|
||||
|
||||
```javascript
|
||||
'furnace': {
|
||||
id: 'furnace',
|
||||
name: 'Peč',
|
||||
ingredients: [
|
||||
{ item: 'stone', amount: 8 },
|
||||
{ item: 'coal', amount: 4 }
|
||||
],
|
||||
result: { item: 'furnace', amount: 1 },
|
||||
category: 'workstation',
|
||||
description: 'Topi rude v ingote.'
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
---
|
||||
|
||||
## 🔄 Crafting Funkcija Primerjava
|
||||
|
||||
### **Obstoječa Funkcija (Podroben Format)**
|
||||
|
||||
```javascript
|
||||
/**
|
||||
* Helper function to craft item (consumes ingredients)
|
||||
*/
|
||||
function craftItem(recipeId, playerInventory) {
|
||||
if (!canCraft(recipeId, playerInventory)) {
|
||||
console.warn('⚠️ Cannot craft - missing ingredients!');
|
||||
return false;
|
||||
}
|
||||
|
||||
const recipe = getCraftingRecipe(recipeId);
|
||||
|
||||
// Consume ingredients
|
||||
for (const ingredient of recipe.ingredients) {
|
||||
playerInventory.removeItem(ingredient.item, ingredient.amount);
|
||||
}
|
||||
|
||||
// Add result
|
||||
playerInventory.addItem(recipe.result.item, recipe.result.amount);
|
||||
|
||||
console.log(`✅ Crafted ${recipe.result.amount}x ${recipe.name}`);
|
||||
return true;
|
||||
}
|
||||
|
||||
// UPORABA:
|
||||
craftItem('lesena_ograda', playerInventory);
|
||||
// Rezultat: wood -= 2, lesena_ograda += 5
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### **Enostavna Funkcija (Enostaven Format)**
|
||||
|
||||
```javascript
|
||||
/**
|
||||
* Poskuša izdelati določen predmet (Enostaven Format).
|
||||
* @param {string} recipeKey - Ime recepta (npr. 'lesena_ograda').
|
||||
*/
|
||||
function craftItem(recipeKey) {
|
||||
const recipe = craftingRecipes[recipeKey];
|
||||
|
||||
if (!recipe) {
|
||||
console.error(`Recept '${recipeKey}' ne obstaja.`);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Preverimo, ali imamo dovolj vseh sestavin
|
||||
for (const requiredItem in recipe.requires) {
|
||||
const requiredAmount = recipe.requires[requiredItem];
|
||||
|
||||
// Če je predmeta manj v inventarju, ne moremo craftati!
|
||||
if (inventory[requiredItem] === undefined || inventory[requiredItem] < requiredAmount) {
|
||||
console.warn(`Ni dovolj ${requiredItem} za izdelavo ${recipeKey}. Potrebno: ${requiredAmount}, Imate: ${inventory[requiredItem] || 0}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Če pridemo sem, imamo dovolj sestavin:
|
||||
|
||||
// 1. Odstranimo porabljene sestavine iz inventarja
|
||||
for (const requiredItem in recipe.requires) {
|
||||
const requiredAmount = recipe.requires[requiredItem];
|
||||
inventory[requiredItem] -= requiredAmount;
|
||||
}
|
||||
|
||||
// 2. Dodamo izdelan predmet v inventar
|
||||
addItemToInventory(recipeKey, recipe.yields);
|
||||
|
||||
console.log(`${recipeKey} uspešno izdelan!`);
|
||||
return true;
|
||||
}
|
||||
|
||||
// UPORABA:
|
||||
craftItem('lesena_ograda');
|
||||
// Rezultat: inventory.wood -= 2, inventory.lesena_ograda += 5
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### **Primerjava Funkcij**
|
||||
|
||||
| Lastnost | Obstoječa | Enostavna |
|
||||
|----------|-----------|-----------|
|
||||
| Format receptov | `ingredients: [{item, amount}]` | `requires: {item: amount}` |
|
||||
| Inventory sistem | Uporablja `playerInventory` objekt | Uporablja globalni `inventory` objekt |
|
||||
| Preverjanje | `canCraft()` helper funkcija | Direktno v funkciji |
|
||||
| Fleksibilnost | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ |
|
||||
| Enostavnost | ⭐⭐⭐ | ⭐⭐⭐⭐⭐ |
|
||||
|
||||
---
|
||||
|
||||
### **Kdy Uporabiti Katero?**
|
||||
|
||||
**Uporabi OBSTOJEČO funkcijo** če:
|
||||
- ✅ Uporabljaš InventorySystem class
|
||||
- ✅ Rabiš kategorije in opise
|
||||
- ✅ Delaš kompleksnejšo igro
|
||||
- ✅ Rabiš helper funkcije (`canCraft`, `getCraftingRecipe`)
|
||||
|
||||
**Uporabi ENOSTAVNO funkcijo** če:
|
||||
- ✅ Imaš preprost inventory (object)
|
||||
- ✅ Delaš prototip/testiranje
|
||||
- ✅ Rabiš hitro implementacijo
|
||||
- ✅ Ne rabiš kategorij
|
||||
|
||||
---
|
||||
|
||||
## 🔍 Debugging
|
||||
|
||||
### **Problem: Recept ne deluje**
|
||||
|
||||
```javascript
|
||||
// Preveri če recept obstaja
|
||||
const recipe = getCraftingRecipe('moj_recept');
|
||||
if (!recipe) {
|
||||
console.error('❌ Recept ne obstaja!');
|
||||
}
|
||||
|
||||
// Preveri ingrediente
|
||||
const canMake = canCraft('moj_recept', playerInventory);
|
||||
console.log('Can craft:', canMake);
|
||||
|
||||
// Preveri inventory
|
||||
console.log('Player wood:', playerInventory.getItemCount('wood'));
|
||||
```
|
||||
|
||||
### **Problem: Material se ne porabi**
|
||||
|
||||
```javascript
|
||||
// Poglej craftItem funkcijo v CraftingRecipes.js
|
||||
// Preveri če removeItem() deluje:
|
||||
playerInventory.removeItem('wood', 2);
|
||||
console.log('Wood after:', playerInventory.getItemCount('wood'));
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎨 Best Practices
|
||||
|
||||
### ✅ DO:
|
||||
- Uporabi smiselne ID-je (`stone_axe`, ne `item_123`)
|
||||
- Dodaj opise za vse recepte
|
||||
- Uporabi ustrezne kategorije
|
||||
- Testiraj recepte pred objavo
|
||||
|
||||
### ❌ DON'T:
|
||||
- Ne uporabljaj presledkov v ID-jih (`stone axe` ❌)
|
||||
- Ne pozabi `yields`/`result.amount`
|
||||
- Ne uporabljaj item-ov ki ne obstajajo
|
||||
|
||||
---
|
||||
|
||||
## 📊 Obstoječi Recepti
|
||||
|
||||
Trenutno v igri:
|
||||
|
||||
| Recept | Kategorija | Ingredienti |
|
||||
|--------|-----------|-------------|
|
||||
| Bone Pickaxe | tools | bone (3), wood (2) |
|
||||
| Bone Axe | tools | bone (3), wood (2) |
|
||||
| Bone Hoe | tools | bone (2), wood (2) |
|
||||
| Bone Sword | weapons | bone (2), wood (1) |
|
||||
| Wooden Pickaxe | tools | wood (3), stick (2) |
|
||||
| Stone Pickaxe | tools | stone (3), stick (2) |
|
||||
| Stick | materials | wood (1) → 4x |
|
||||
| Torch | lighting | stick (1), coal (1) → 4x |
|
||||
| Chest | storage | wood (8) |
|
||||
| Furnace | workstation | stone (8), coal (4) |
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Naslednji Koraki
|
||||
|
||||
1. **Odpri** `c:\novafarma\src\data\CraftingRecipes.js`
|
||||
2. **Dodaj** svoje recepte v ustrezno kategorijo
|
||||
3. **Shrani** datoteko
|
||||
4. **Osveži** igro (Ctrl+R)
|
||||
5. **Testiraj** v crafting meniju (C tipka)
|
||||
|
||||
---
|
||||
|
||||
**Zadnja posodobitev:** 14.12.2025
|
||||
**Avtor:** KRVAVA ŽETEV Team
|
||||
**Status:** ✅ Ready to use
|
||||
**Lokacija:** `c:\novafarma\src\data\CraftingRecipes.js`
|
||||
312
docs/Z_SORTING_GUIDE.md
Normal file
312
docs/Z_SORTING_GUIDE.md
Normal file
@@ -0,0 +1,312 @@
|
||||
# 🎯 Z-Sorting System Guide (2.5D Depth)
|
||||
|
||||
Vodič za obvladovanje globine objektov v 2.5D isometric igrah.
|
||||
|
||||
---
|
||||
|
||||
## 📖 Kaj je Z-Sorting?
|
||||
|
||||
V 2.5D isometric igrah moramo objekte **pravilno prekrivati** glede na njihovo pozicijo:
|
||||
- Objekti **spredaj** (višji Y) morajo biti **nad** objekti zadaj (nižji Y)
|
||||
- To se doseže z nastavitvijo **depth** vrednosti
|
||||
|
||||
**Primer:**
|
||||
```
|
||||
Drevo na Y=100 mora biti NAD karakterjem na Y=50
|
||||
Karakter na Y=100 mora biti NAD drevesom na Y=50
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔄 DVA PRISTOPA
|
||||
|
||||
### **Pristop 1: Manual updateDepth()** ❌ STAR SISTEM
|
||||
|
||||
**Kako deluje:**
|
||||
```javascript
|
||||
updateDepth() {
|
||||
const layerBase = 200000; // LAYER_OBJECTS
|
||||
const depth = layerBase + this.sprite.y;
|
||||
this.sprite.setDepth(depth);
|
||||
}
|
||||
```
|
||||
|
||||
**Klici:**
|
||||
- V `update()` funkciji vsake Entity
|
||||
- Ko se objekt premakne
|
||||
|
||||
**Prednosti:**
|
||||
- ✅ Zelo natančno (vsak piksel šteje)
|
||||
- ✅ Deluje z layerji (LAYER_TERRAIN, LAYER_OBJECTS, LAYER_UI)
|
||||
|
||||
**Slabosti:**
|
||||
- ❌ Vsak objekt potrebuje svojo `updateDepth()` funkcijo
|
||||
- ❌ Več kode za vzdrževanje
|
||||
- ❌ Moral klicat ročno
|
||||
|
||||
---
|
||||
|
||||
### **Pristop 2: sortableObjects Group** ✅ NOV SISTEM
|
||||
|
||||
**Kako deluje:**
|
||||
```javascript
|
||||
// V create() - GameScene.js
|
||||
this.sortableObjects = this.add.group();
|
||||
this.sortableObjects.add(this.player.sprite);
|
||||
|
||||
// V update() - GameScene.js
|
||||
const children = this.sortableObjects.getChildren();
|
||||
children.sort((a, b) => a.y - b.y); // Sortiranje po Y
|
||||
children.forEach((obj, index) => {
|
||||
obj.setDepth(200000 + index);
|
||||
});
|
||||
```
|
||||
|
||||
**Prednosti:**
|
||||
- ✅ **Enostavno** - samo dodaš sprite v group
|
||||
- ✅ **Centralizirano** - vse na enem mestu
|
||||
- ✅ **Avtomatsko** - sortira se vsak frame
|
||||
|
||||
**Slabosti:**
|
||||
- ⚠️ Sortira VSE objekte vsak frame (lahko počasno pri 100+ objektih)
|
||||
- ⚠️ Depth je samo index (0, 1, 2, 3...) ne natančen Y
|
||||
|
||||
---
|
||||
|
||||
## 🛠️ Implementacija (Korak-po-Korak)
|
||||
|
||||
### **Korak 1: Ustvari sortableObjects Group**
|
||||
|
||||
V `GameScene.js` - `create()` funkciji:
|
||||
|
||||
```javascript
|
||||
// Po ustvarjanju playerja
|
||||
this.player = new Player(this, 50, 50, offsetX, offsetY);
|
||||
|
||||
// 🎯 SORTABLE OBJECTS GROUP
|
||||
console.log('🎯 Creating sortableObjects group...');
|
||||
this.sortableObjects = this.add.group();
|
||||
|
||||
// Dodaj player sprite
|
||||
if (this.player && this.player.sprite) {
|
||||
this.sortableObjects.add(this.player.sprite);
|
||||
}
|
||||
```
|
||||
|
||||
### **Korak 2: Dodaj Z-Sorting v Update**
|
||||
|
||||
V `GameScene.js` - `update()` funkciji:
|
||||
|
||||
```javascript
|
||||
update(time, delta) {
|
||||
if (this.player) this.player.update(delta);
|
||||
|
||||
// 🎯 Z-SORTING
|
||||
if (this.sortableObjects) {
|
||||
const children = this.sortableObjects.getChildren();
|
||||
|
||||
// Sortiranje po Y koordinati
|
||||
children.sort((a, b) => a.y - b.y);
|
||||
|
||||
// Nastavi depth glede na vrstni red
|
||||
children.forEach((obj, index) => {
|
||||
obj.setDepth(200000 + index); // LAYER_OBJECTS base
|
||||
});
|
||||
}
|
||||
|
||||
// ... ostali sistemi
|
||||
}
|
||||
```
|
||||
|
||||
### **Korak 3: Odstrani Manual updateDepth()**
|
||||
|
||||
V `Player.js` (ali drugih Entity classes):
|
||||
|
||||
```javascript
|
||||
update(delta) {
|
||||
// ❌ ODSTRANI TO:
|
||||
// if (this.isMoving) {
|
||||
// this.updateDepth();
|
||||
// }
|
||||
|
||||
// ✅ Ne rabiš več - sortableObjects to naredi avtomatsko!
|
||||
|
||||
if (!this.isMoving) {
|
||||
this.handleInput();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### **Korak 4: Dodaj Nove Objekte (Drevesa, NPCji, itd.)**
|
||||
|
||||
Ko ustvarjaš nov objekt:
|
||||
|
||||
```javascript
|
||||
// PRIMER: Dodaj drevo
|
||||
const tree = this.add.sprite(x, y, 'tree_sprite');
|
||||
tree.setOrigin(0.5, 1.0);
|
||||
|
||||
// ✨ DODAJ V SORTABLE OBJECTS
|
||||
this.sortableObjects.add(tree);
|
||||
|
||||
// PRIMER: Dodaj NPC
|
||||
const npc = new NPC(this, gridX, gridY);
|
||||
this.sortableObjects.add(npc.sprite);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎮 Praktični Primeri
|
||||
|
||||
### **Primer 1: Player + Drevesa**
|
||||
|
||||
```javascript
|
||||
// V create()
|
||||
this.player = new Player(this, 50, 50, offsetX, offsetY);
|
||||
this.sortableObjects = this.add.group();
|
||||
this.sortableObjects.add(this.player.sprite);
|
||||
|
||||
// Dodaj 5 dreves
|
||||
for (let i = 0; i < 5; i++) {
|
||||
const tree = this.add.sprite(
|
||||
Math.random() * 500,
|
||||
Math.random() * 500,
|
||||
'tree_sprite'
|
||||
);
|
||||
tree.setOrigin(0.5, 1.0);
|
||||
this.sortableObjects.add(tree); // ✨ Avtomatično sortiranje!
|
||||
}
|
||||
```
|
||||
|
||||
### **Primer 2: Player + NPCs + Drevesa**
|
||||
|
||||
```javascript
|
||||
// Player
|
||||
this.sortableObjects.add(this.player.sprite);
|
||||
|
||||
// NPCs
|
||||
this.npcs = [];
|
||||
for (let i = 0; i < 10; i++) {
|
||||
const npc = new NPC(this, randX, randY);
|
||||
this.npcs.push(npc);
|
||||
this.sortableObjects.add(npc.sprite); // ✨
|
||||
}
|
||||
|
||||
// Drevesa
|
||||
this.trees.forEach(tree => {
|
||||
this.sortableObjects.add(tree.sprite); // ✨
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ⚡ Performance Tips
|
||||
|
||||
### **Problem: Počasno pri 100+ objektih**
|
||||
|
||||
Če imaš **več kot 100 objektov**, sortiranje vsak frame (60x/sekundo) je lahko počasno.
|
||||
|
||||
**Rešitev 1: Dirty Flag**
|
||||
```javascript
|
||||
update(time, delta) {
|
||||
// Sortiranje samo ko potrebno, ne vsak frame
|
||||
if (this.needsSorting) {
|
||||
const children = this.sortableObjects.getChildren();
|
||||
children.sort((a, b) => a.y - b.y);
|
||||
children.forEach((obj, index) => obj.setDepth(200000 + index));
|
||||
this.needsSorting = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Nastavi v Player.moveToGrid() ali NPC movement
|
||||
moveToGrid(targetX, targetY) {
|
||||
// ... movement code
|
||||
this.scene.needsSorting = true; // ✅ Označi za sortiranje
|
||||
}
|
||||
```
|
||||
|
||||
**Rešitev 2: Throttling (vsak N-ti frame)**
|
||||
```javascript
|
||||
update(time, delta) {
|
||||
this.sortCounter = (this.sortCounter || 0) + 1;
|
||||
|
||||
// Sortiranje samo vsak 3. frame (namesto vsakega)
|
||||
if (this.sortCounter % 3 === 0) {
|
||||
const children = this.sortableObjects.getChildren();
|
||||
children.sort((a, b) => a.y - b.y);
|
||||
children.forEach((obj, index) => obj.setDepth(200000 + index));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔍 Debugging
|
||||
|
||||
### **Problem: Objekti ne prekrivajo pravilno**
|
||||
|
||||
```javascript
|
||||
// V update(), dodaj console.log
|
||||
update(time, delta) {
|
||||
if (this.sortableObjects) {
|
||||
const children = this.sortableObjects.getChildren();
|
||||
children.sort((a, b) => a.y - b.y);
|
||||
children.forEach((obj, index) => {
|
||||
obj.setDepth(200000 + index);
|
||||
|
||||
// 🔍 DEBUG
|
||||
console.log(`${obj.texture.key}: Y=${obj.y}, Depth=${obj.depth}`);
|
||||
});
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### **Problem: Objekti manjkajo**
|
||||
|
||||
Preveri če si dodal sprite v group:
|
||||
```javascript
|
||||
// ✅ PRAVILNO
|
||||
this.sortableObjects.add(this.player.sprite);
|
||||
|
||||
// ❌ NAROBE
|
||||
this.sortableObjects.add(this.player); // Mora biti .sprite!
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📊 Primerjava: Manual vs. SortableObjects
|
||||
|
||||
| Feature | Manual updateDepth() | sortableObjects |
|
||||
|---------|---------------------|-----------------|
|
||||
| **Enostavnost** | ⭐⭐ | ⭐⭐⭐⭐⭐ |
|
||||
| **Performance (< 50 obj)** | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ |
|
||||
| **Performance (> 100 obj)** | ⭐⭐⭐⭐ | ⭐⭐ |
|
||||
| **Natančnost** | ⭐⭐⭐⭐⭐ (pixel perfect) | ⭐⭐⭐ (index based) |
|
||||
| **Vzdrževanje** | ⭐⭐ (več kode) | ⭐⭐⭐⭐⭐ (ena funkcija) |
|
||||
| **Layer podpora** | ✅ Da | ⚠️ Samo eno layer |
|
||||
|
||||
---
|
||||
|
||||
## 🎯 PRIPOROČILO
|
||||
|
||||
### **Za KRVAVA ŽETEV:**
|
||||
✅ **Uporabljaj sortableObjects** - enostavnejše in hitrejše za razvoj
|
||||
|
||||
### **Kdaj Manual:**
|
||||
- Zelo kompleksne igre (10+ layerjev)
|
||||
- Več kot 200+ objektov
|
||||
- Potrebuješ pixel-perfect depth
|
||||
|
||||
---
|
||||
|
||||
## 📚 Dodatno Branje
|
||||
|
||||
- [Phaser Groups Documentation](https://photonstorm.github.io/phaser3-docs/Phaser.GameObjects.Group.html)
|
||||
- [Isometric Depth Sorting](https://www.youtube.com/watch?v=04oQ2jOUjkU)
|
||||
- [Advanced Z-Sorting Techniques](https://gamedev.stackexchange.com/questions/8/how-do-i-sort-isometric-sprites-into-the-correct-order)
|
||||
|
||||
---
|
||||
|
||||
**Zadnja posodobitev:** 14.12.2025
|
||||
**Avtor:** KRVAVA ŽETEV Team
|
||||
**Status:** ✅ Implementirano v GameScene.js
|
||||
@@ -127,6 +127,31 @@ const CRAFTING_RECIPES = {
|
||||
result: { item: 'furnace', amount: 1 },
|
||||
category: 'workstation',
|
||||
description: 'Smelts ores into bars.'
|
||||
},
|
||||
|
||||
// ==================================
|
||||
// CUSTOM RECIPES (NEW!)
|
||||
// ==================================
|
||||
'lesena_ograda': {
|
||||
id: 'lesena_ograda',
|
||||
name: 'Lesena Ograda',
|
||||
ingredients: [
|
||||
{ item: 'wood', amount: 2 }
|
||||
],
|
||||
result: { item: 'lesena_ograda', amount: 5 },
|
||||
category: 'buildings',
|
||||
description: 'Lesena ograja za zaščito farme.'
|
||||
},
|
||||
'sekira_osnovna': {
|
||||
id: 'sekira_osnovna',
|
||||
name: 'Osnovna Sekira',
|
||||
ingredients: [
|
||||
{ item: 'wood', amount: 3 },
|
||||
{ item: 'stone', amount: 2 }
|
||||
],
|
||||
result: { item: 'sekira_osnovna', amount: 1 },
|
||||
category: 'tools',
|
||||
description: 'Osnovno orodje za sekanje dreves.'
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -222,9 +222,10 @@ class Player {
|
||||
}
|
||||
|
||||
update(delta) {
|
||||
if (this.isMoving) {
|
||||
this.updateDepth();
|
||||
}
|
||||
// NOTE: updateDepth() disabled - using sortableObjects Z-sorting in GameScene
|
||||
// if (this.isMoving) {
|
||||
// this.updateDepth();
|
||||
// }
|
||||
|
||||
if (!this.isMoving) {
|
||||
this.handleInput();
|
||||
@@ -506,7 +507,8 @@ class Player {
|
||||
}
|
||||
});
|
||||
|
||||
this.updateDepth();
|
||||
// NOTE: updateDepth() disabled - using sortableObjects Z-sorting in GameScene
|
||||
// this.updateDepth();
|
||||
}
|
||||
|
||||
updatePosition() {
|
||||
|
||||
@@ -338,6 +338,16 @@ class GameScene extends Phaser.Scene {
|
||||
console.log('👤 Initializing player...');
|
||||
this.player = new Player(this, 50, 50, this.terrainOffsetX, this.terrainOffsetY);
|
||||
|
||||
// 🎯 SORTABLE OBJECTS GROUP - Za 2.5D Z-Sorting
|
||||
console.log('🎯 Creating sortableObjects group for Z-sorting...');
|
||||
this.sortableObjects = this.add.group();
|
||||
|
||||
// Dodaj player sprite v sortableObjects
|
||||
if (this.player && this.player.sprite) {
|
||||
this.sortableObjects.add(this.player.sprite);
|
||||
console.log('✅ Player added to sortableObjects');
|
||||
}
|
||||
|
||||
// ALL NPCs REMOVED - Solo farming mode
|
||||
console.log('🌾 Solo farming mode - no NPCs');
|
||||
|
||||
@@ -838,6 +848,20 @@ class GameScene extends Phaser.Scene {
|
||||
update(time, delta) {
|
||||
if (this.player) this.player.update(delta);
|
||||
|
||||
// 🎯 Z-SORTING: SortableObjects based on Y position
|
||||
if (this.sortableObjects) {
|
||||
const children = this.sortableObjects.getChildren();
|
||||
|
||||
// Sortiranje po Y koordinati (nižji Y = nižji depth)
|
||||
children.sort((a, b) => a.y - b.y);
|
||||
|
||||
// Nastavi depth glede na vrstni red (index)
|
||||
children.forEach((obj, index) => {
|
||||
// Use LAYER_OBJECTS base + index for depth
|
||||
obj.setDepth(200000 + index);
|
||||
});
|
||||
}
|
||||
|
||||
// Update Systems
|
||||
if (this.terrainSystem) this.terrainSystem.update(time, delta); // Water animation!
|
||||
if (this.statsSystem) this.statsSystem.update(delta);
|
||||
|
||||
Reference in New Issue
Block a user