kockasta mapa
This commit is contained in:
@@ -17,9 +17,6 @@ class InteractionSystem {
|
||||
this.scene.input.keyboard.on('keydown-E', () => {
|
||||
this.handleInteractKey();
|
||||
});
|
||||
|
||||
// Loot Array
|
||||
this.drops = [];
|
||||
}
|
||||
|
||||
handleInteractKey() {
|
||||
@@ -30,7 +27,9 @@ class InteractionSystem {
|
||||
let nearest = null;
|
||||
let minDist = 2.5; // Interaction range
|
||||
|
||||
for (const npc of this.scene.npcs) {
|
||||
const candidates = this.scene.spatialGrid ? this.scene.spatialGrid.query(playerPos.x, playerPos.y) : this.scene.npcs;
|
||||
|
||||
for (const npc of candidates) {
|
||||
const d = Phaser.Math.Distance.Between(playerPos.x, playerPos.y, npc.gridX, npc.gridY);
|
||||
if (d < minDist) {
|
||||
minDist = d;
|
||||
@@ -84,8 +83,10 @@ class InteractionSystem {
|
||||
}
|
||||
|
||||
// 3.5 Check for NPC Interaction
|
||||
if (this.scene.npcs) {
|
||||
for (const npc of this.scene.npcs) {
|
||||
const candidates = this.scene.spatialGrid ? this.scene.spatialGrid.query(gridX, gridY) : this.scene.npcs;
|
||||
|
||||
if (candidates) {
|
||||
for (const npc of candidates) {
|
||||
// Increased radius to 1.8 to catch moving NPCs easier
|
||||
if (Math.abs(npc.gridX - gridX) < 1.8 && Math.abs(npc.gridY - gridY) < 1.8) {
|
||||
|
||||
@@ -139,11 +140,9 @@ class InteractionSystem {
|
||||
|
||||
if (decor.type === 'tree') {
|
||||
damage = (activeTool === 'axe') ? 3 : 1;
|
||||
if (!isAttack && activeTool !== 'axe') return;
|
||||
}
|
||||
else if (decor.type === 'stone') {
|
||||
damage = (activeTool === 'pickaxe') ? 3 : 1;
|
||||
if (!isAttack && activeTool !== 'pickaxe') return;
|
||||
}
|
||||
else if (decor.type === 'bush') damage = 2;
|
||||
|
||||
@@ -153,17 +152,18 @@ class InteractionSystem {
|
||||
|
||||
if (decor.hp <= 0) {
|
||||
const type = this.scene.terrainSystem.removeDecoration(gridX, gridY);
|
||||
// Loot logic
|
||||
// Loot logic via LootSystem
|
||||
let loot = 'wood';
|
||||
if (type === 'stone') loot = 'stone';
|
||||
if (type === 'bush') loot = 'seeds'; // Maybe berries?
|
||||
if (type === 'tree') {
|
||||
this.spawnLoot(gridX, gridY, 'wood');
|
||||
this.spawnLoot(gridX, gridY, 'wood');
|
||||
this.spawnLoot(gridX, gridY, 'wood');
|
||||
}
|
||||
else {
|
||||
this.spawnLoot(gridX, gridY, loot);
|
||||
|
||||
if (this.scene.lootSystem) {
|
||||
if (type === 'tree') {
|
||||
this.scene.lootSystem.spawnLoot(gridX, gridY, 'wood', 3);
|
||||
}
|
||||
else {
|
||||
this.scene.lootSystem.spawnLoot(gridX, gridY, loot, 1);
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
@@ -192,42 +192,7 @@ class InteractionSystem {
|
||||
this.scene.tweens.add({ targets: txt, y: txt.y - 40, alpha: 0, duration: 1000, onComplete: () => txt.destroy() });
|
||||
}
|
||||
|
||||
spawnLoot(gridX, gridY, type) {
|
||||
console.log(`🎁 Spawning ${type} at ${gridX},${gridY}`);
|
||||
|
||||
const screenPos = this.iso.toScreen(gridX, gridY);
|
||||
const x = screenPos.x + this.scene.terrainOffsetX;
|
||||
const y = screenPos.y + this.scene.terrainOffsetY;
|
||||
|
||||
let symbol = '?';
|
||||
if (type === 'wood') symbol = '🪵';
|
||||
if (type === 'stone') symbol = '🪨';
|
||||
if (type === 'seeds') symbol = '🌱';
|
||||
if (type === 'wheat') symbol = '🌾';
|
||||
if (type === 'axe') symbol = '🪓';
|
||||
if (type === 'item_bone') symbol = '🦴';
|
||||
|
||||
const drop = this.scene.add.text(x, y - 20, symbol, { fontSize: '20px' });
|
||||
drop.setOrigin(0.5);
|
||||
drop.setDepth(this.iso.getDepth(gridX, gridY) + 500);
|
||||
|
||||
this.scene.tweens.add({
|
||||
targets: drop, y: y - 40, duration: 500, yoyo: true, ease: 'Sine.easeOut', repeat: -1
|
||||
});
|
||||
|
||||
this.drops.push({ gridX, gridY, sprite: drop, type: type });
|
||||
}
|
||||
|
||||
update() {
|
||||
if (!this.scene.player) return;
|
||||
const playerPos = this.scene.player.getPosition();
|
||||
for (let i = this.drops.length - 1; i >= 0; i--) {
|
||||
const drop = this.drops[i];
|
||||
if (Math.abs(drop.gridX - playerPos.x) < 0.8 && Math.abs(drop.gridY - playerPos.y) < 0.8) {
|
||||
if (this.scene.inventorySystem) this.scene.inventorySystem.addItem(drop.type, 1);
|
||||
drop.sprite.destroy();
|
||||
this.drops.splice(i, 1);
|
||||
}
|
||||
}
|
||||
update(delta) {
|
||||
// No logic needed here anymore (loot pickup handled by LootSystem)
|
||||
}
|
||||
}
|
||||
|
||||
118
src/systems/LootSystem.js
Normal file
118
src/systems/LootSystem.js
Normal file
@@ -0,0 +1,118 @@
|
||||
class LootSystem {
|
||||
constructor(scene) {
|
||||
this.scene = scene;
|
||||
this.drops = []; // Active loot drops
|
||||
this.iso = scene.terrainSystem ? scene.terrainSystem.iso : null; // Reference to IsoUtils
|
||||
|
||||
// Settings
|
||||
this.pickupRadius = 1.0; // Grid based
|
||||
this.magnetRadius = 3.0; // Optional: fly towards player
|
||||
}
|
||||
|
||||
spawnLoot(gridX, gridY, type, count = 1) {
|
||||
if (!this.iso && this.scene.terrainSystem) this.iso = this.scene.terrainSystem.iso;
|
||||
if (!this.iso) return; // Safety check
|
||||
|
||||
console.log(`🎁 Spawning ${count}x ${type} at ${gridX},${gridY}`);
|
||||
|
||||
const screenPos = this.iso.toScreen(gridX, gridY);
|
||||
const x = screenPos.x + (this.scene.terrainOffsetX || 0);
|
||||
const y = screenPos.y + (this.scene.terrainOffsetY || 0);
|
||||
|
||||
// Visual Symbol Mapping
|
||||
let symbol = '?';
|
||||
const symbols = {
|
||||
'wood': '🪵', 'stone': '🪨', 'seeds': '🌱', 'wheat': '🌾',
|
||||
'axe': '🪓', 'pickaxe': '⛏️', 'sword': '⚔️', 'hoe': '🚜',
|
||||
'item_bone': '🦴', 'flower': '🌸'
|
||||
};
|
||||
if (symbols[type]) symbol = symbols[type];
|
||||
|
||||
// Create Sprite/Text
|
||||
// Using Text for now as it supports emojis easily, but could be Sprite
|
||||
const drop = this.scene.add.text(x, y - 20, symbol, { fontSize: '20px' });
|
||||
drop.setOrigin(0.5);
|
||||
drop.setDepth(this.iso.getDepth(gridX, gridY) + 500);
|
||||
|
||||
// Animation (Bobbing)
|
||||
this.scene.tweens.add({
|
||||
targets: drop,
|
||||
y: y - 40,
|
||||
duration: 600,
|
||||
yoyo: true,
|
||||
ease: 'Sine.easeInOut',
|
||||
repeat: -1
|
||||
});
|
||||
|
||||
// Add to list
|
||||
this.drops.push({
|
||||
gridX, gridY,
|
||||
x, y,
|
||||
sprite: drop,
|
||||
type: type,
|
||||
count: count,
|
||||
spawnTime: Date.now()
|
||||
});
|
||||
}
|
||||
|
||||
update() {
|
||||
if (!this.scene.player) return;
|
||||
|
||||
const playerPos = this.scene.player.getPosition();
|
||||
|
||||
// Loop backwards to allow removal
|
||||
for (let i = this.drops.length - 1; i >= 0; i--) {
|
||||
const drop = this.drops[i];
|
||||
|
||||
// Distance Check
|
||||
const dist = Math.abs(drop.gridX - playerPos.x) + Math.abs(drop.gridY - playerPos.y); // Manhattan-ish
|
||||
|
||||
// Pickup Logic
|
||||
if (dist < 0.8) {
|
||||
this.collectLoot(drop, i);
|
||||
}
|
||||
// Magnet Logic (Visual fly to player) - Optional
|
||||
else if (dist < 3.0) {
|
||||
// Move visual slightly towards player?
|
||||
// Implementing full physics/velocity might be overkill for now.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
collectLoot(drop, index) {
|
||||
if (this.scene.inventorySystem) {
|
||||
const leftover = this.scene.inventorySystem.addItem(drop.type, drop.count);
|
||||
|
||||
if (leftover === 0) {
|
||||
// Success
|
||||
this.scene.sound.play('pickup_sound')
|
||||
// (Assuming sound exists, if not it will just warn silently or fail)
|
||||
// Actually, let's skip sound call if not sure to avoid error spam
|
||||
|
||||
// Float text effect
|
||||
this.showFloatingText(`+${drop.count} ${drop.type}`, drop.x, drop.y);
|
||||
|
||||
// Remove
|
||||
drop.sprite.destroy();
|
||||
this.drops.splice(index, 1);
|
||||
} else {
|
||||
// Config full? Update count?
|
||||
drop.count = leftover;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
showFloatingText(text, x, y) {
|
||||
const txt = this.scene.add.text(x, y - 50, text, {
|
||||
fontSize: '14px', fill: '#ffff00', stroke: '#000', strokeThickness: 2
|
||||
}).setOrigin(0.5).setDepth(20000);
|
||||
|
||||
this.scene.tweens.add({
|
||||
targets: txt,
|
||||
y: y - 100,
|
||||
alpha: 0,
|
||||
duration: 800,
|
||||
onComplete: () => txt.destroy()
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -10,276 +10,150 @@ class TerrainSystem {
|
||||
this.noise = new PerlinNoise(Date.now());
|
||||
|
||||
this.tiles = [];
|
||||
this.decorations = []; // Array za save/load compat
|
||||
this.decorationsMap = new Map(); // Fast lookup key->decor
|
||||
this.cropsMap = new Map(); // Store dynamic crops separately
|
||||
// Render state monitoring
|
||||
this.visibleTiles = new Map(); // Key: "x,y", Value: Sprite
|
||||
this.visibleDecorations = new Map(); // Key: "x,y", Value: Sprite
|
||||
this.visibleCrops = new Map(); // Key: "x,y", Value: Sprite
|
||||
this.decorations = [];
|
||||
this.decorationsMap = new Map();
|
||||
this.cropsMap = new Map();
|
||||
|
||||
this.visibleTiles = new Map();
|
||||
this.visibleDecorations = new Map();
|
||||
this.visibleCrops = new Map();
|
||||
|
||||
// Pools
|
||||
this.tilePool = {
|
||||
active: [],
|
||||
inactive: [],
|
||||
get: () => {
|
||||
if (this.tilePool.inactive.length > 0) {
|
||||
const s = this.tilePool.inactive.pop();
|
||||
s.setVisible(true);
|
||||
return s;
|
||||
}
|
||||
const s = this.scene.add.sprite(0, 0, 'dirt');
|
||||
s.setOrigin(0.5, 0.5);
|
||||
return s;
|
||||
},
|
||||
release: (sprite) => {
|
||||
sprite.setVisible(false);
|
||||
this.tilePool.inactive.push(sprite);
|
||||
}
|
||||
};
|
||||
|
||||
this.decorationPool = {
|
||||
active: [],
|
||||
inactive: [],
|
||||
get: () => {
|
||||
if (this.decorationPool.inactive.length > 0) {
|
||||
const s = this.decorationPool.inactive.pop();
|
||||
s.setVisible(true);
|
||||
s.clearTint();
|
||||
return s;
|
||||
}
|
||||
return this.scene.add.sprite(0, 0, 'tree');
|
||||
},
|
||||
release: (sprite) => {
|
||||
sprite.setVisible(false);
|
||||
this.decorationPool.inactive.push(sprite);
|
||||
}
|
||||
};
|
||||
|
||||
this.cropPool = {
|
||||
active: [],
|
||||
inactive: [],
|
||||
get: () => {
|
||||
if (this.cropPool.inactive.length > 0) {
|
||||
const s = this.cropPool.inactive.pop();
|
||||
s.setVisible(true);
|
||||
return s;
|
||||
}
|
||||
return this.scene.add.sprite(0, 0, 'crop_stage_1');
|
||||
},
|
||||
release: (sprite) => {
|
||||
sprite.setVisible(false);
|
||||
this.cropPool.inactive.push(sprite);
|
||||
}
|
||||
};
|
||||
|
||||
this.terrainTypes = {
|
||||
WATER: { name: 'water', height: 0, color: 0x4444ff },
|
||||
SAND: { name: 'sand', height: 0.2, color: 0xdddd44 },
|
||||
GRASS_FULL: { name: 'grass_full', height: 0.35, color: 0x44aa44 },
|
||||
GRASS_TOP: { name: 'grass_top', height: 0.45, color: 0x66cc66 },
|
||||
DIRT: { name: 'dirt', height: 0.5, color: 0x8b4513 },
|
||||
STONE: { name: 'stone', height: 0.7, color: 0x888888 },
|
||||
PATH: { name: 'path', height: -1, color: 0xc2b280 },
|
||||
FARMLAND: { name: 'farmland', height: -1, color: 0x5c4033 }
|
||||
};
|
||||
|
||||
this.offsetX = 0;
|
||||
this.offsetY = 0;
|
||||
|
||||
// Culling optimization
|
||||
this.lastCullX = -9999;
|
||||
this.lastCullY = -9999;
|
||||
|
||||
// Object Pools
|
||||
this.tilePool = new ObjectPool(
|
||||
() => {
|
||||
const sprite = this.scene.add.image(0, 0, 'tile_grass');
|
||||
sprite.setOrigin(0.5, 0); // Isometrični tiles imajo origin zgoraj/center ali po potrebi
|
||||
return sprite;
|
||||
},
|
||||
(sprite) => {
|
||||
sprite.setVisible(true);
|
||||
sprite.setAlpha(1);
|
||||
sprite.clearTint();
|
||||
}
|
||||
);
|
||||
|
||||
this.decorationPool = new ObjectPool(
|
||||
() => {
|
||||
const sprite = this.scene.add.sprite(0, 0, 'flower');
|
||||
sprite.setOrigin(0.5, 1);
|
||||
return sprite;
|
||||
},
|
||||
(sprite) => {
|
||||
sprite.setVisible(true);
|
||||
sprite.setAlpha(1);
|
||||
sprite.clearTint(); // Reset damage tint
|
||||
}
|
||||
);
|
||||
|
||||
this.cropPool = new ObjectPool(
|
||||
() => {
|
||||
const sprite = this.scene.add.sprite(0, 0, 'crop_stage_1'); // Default texture logic needed
|
||||
sprite.setOrigin(0.5, 1);
|
||||
return sprite;
|
||||
},
|
||||
(sprite) => {
|
||||
sprite.setVisible(true);
|
||||
sprite.setAlpha(1);
|
||||
}
|
||||
);
|
||||
|
||||
// Tipi terena z threshold vrednostmi + Y-LAYER STACKING
|
||||
this.terrainTypes = {
|
||||
WATER: { threshold: 0.3, color: 0x2166aa, name: 'water', texture: 'tile_water', yLayer: -1 },
|
||||
SAND: { threshold: 0.4, color: 0xf4e7c6, name: 'sand', texture: 'tile_sand', yLayer: 0 },
|
||||
|
||||
// Y-LAYER GRASS VARIANTS (A, B, C systém)
|
||||
GRASS_FULL: { threshold: 0.50, color: 0x5cb85c, name: 'grass_full', texture: 'tile_grass_full', yLayer: 0 }, // A: Full grass
|
||||
GRASS_TOP: { threshold: 0.60, color: 0x5cb85c, name: 'grass_top', texture: 'tile_grass_top', yLayer: 1 }, // B: Grass top, dirt sides
|
||||
DIRT: { threshold: 0.75, color: 0x8b6f47, name: 'dirt', texture: 'tile_dirt', yLayer: 2 }, // C: Full dirt
|
||||
STONE: { threshold: 1.0, color: 0x7d7d7d, name: 'stone', texture: 'tile_stone', yLayer: 3 },
|
||||
|
||||
PATH: { threshold: 999, color: 0x9b7653, name: 'path', texture: 'tile_path', yLayer: 0 }, // Pot/Road
|
||||
FARMLAND: { threshold: 999, color: 0x4a3c2a, name: 'farmland', texture: 'tile_farmland', yLayer: 0 }
|
||||
};
|
||||
}
|
||||
|
||||
// Helper da dobi terrain type glede na elevation (Y-layer)
|
||||
getTerrainTypeByElevation(noiseValue, elevation) {
|
||||
// Osnovni terrain type iz noise
|
||||
let baseType = this.getTerrainType(noiseValue);
|
||||
|
||||
// Če je grass, določi Y-layer variant glede na elevation
|
||||
if (baseType.name.includes('grass') || baseType === this.terrainTypes.GRASS_FULL ||
|
||||
baseType === this.terrainTypes.GRASS_TOP) {
|
||||
|
||||
if (elevation > 0.7) {
|
||||
return this.terrainTypes.GRASS_FULL; // A: Najvišja plast (full grass)
|
||||
} else if (elevation > 0.4) {
|
||||
return this.terrainTypes.GRASS_TOP; // B: Srednja (grass top, dirt sides)
|
||||
} else {
|
||||
return this.terrainTypes.DIRT; // C: Nizka (full dirt)
|
||||
}
|
||||
}
|
||||
|
||||
return baseType;
|
||||
}
|
||||
|
||||
// Helper za določanje tipa terena glede na noise vrednost
|
||||
getTerrainType(value) {
|
||||
if (value < this.terrainTypes.WATER.threshold) return this.terrainTypes.WATER;
|
||||
if (value < this.terrainTypes.SAND.threshold) return this.terrainTypes.SAND;
|
||||
if (value < this.terrainTypes.GRASS_FULL.threshold) return this.terrainTypes.GRASS_FULL; // Fallback grass
|
||||
if (value < this.terrainTypes.GRASS_TOP.threshold) return this.terrainTypes.GRASS_TOP;
|
||||
if (value < this.terrainTypes.DIRT.threshold) return this.terrainTypes.DIRT;
|
||||
return this.terrainTypes.STONE;
|
||||
}
|
||||
|
||||
getTile(x, y) {
|
||||
if (x >= 0 && x < this.width && y >= 0 && y < this.height) {
|
||||
return this.tiles[y][x];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// Generiraj teksture za tiles (da ne uporabljamo Počasnih Graphics objektov)
|
||||
createTileTextures() {
|
||||
console.log('🎨 Creating tile textures...');
|
||||
// Flat Grid Look (No depth)
|
||||
const tileWidth = 48;
|
||||
const tileHeight = 24; // Just the diamond
|
||||
const types = Object.values(this.terrainTypes);
|
||||
|
||||
for (const type of Object.values(this.terrainTypes)) {
|
||||
const key = `tile_${type.name}`;
|
||||
if (this.scene.textures.exists(key)) continue;
|
||||
|
||||
// Check for custom grass tile
|
||||
if (type.name === 'grass' && this.scene.textures.exists('grass_tile')) {
|
||||
this.scene.textures.addImage(key, this.scene.textures.get('grass_tile').getSourceImage());
|
||||
type.texture = key;
|
||||
continue; // Skip procedural generation
|
||||
}
|
||||
|
||||
const tileW = this.iso.tileWidth;
|
||||
const tileH = this.iso.tileHeight;
|
||||
const thickness = 8; // Minimal thickness - nearly 2D
|
||||
types.forEach((type) => {
|
||||
if (this.scene.textures.exists(type.name)) return;
|
||||
|
||||
const graphics = this.scene.make.graphics({ x: 0, y: 0, add: false });
|
||||
|
||||
// Helper for colors
|
||||
const baseColor = Phaser.Display.Color.IntegerToColor(type.color);
|
||||
let darkColor, darkerColor;
|
||||
const x = 0;
|
||||
const top = 0;
|
||||
const midX = 24;
|
||||
const midY = 12;
|
||||
const bottomY = 24;
|
||||
|
||||
// Y-LAYER STACKING: Different side colors based on layer
|
||||
if (type.name === 'grass_full') {
|
||||
// A: Full Grass Block - všechno zeleno
|
||||
darkColor = 0x5cb85c; // Green (lighter)
|
||||
darkerColor = 0x4a9d3f; // Green (darker)
|
||||
} else if (type.name === 'grass_top') {
|
||||
// B: Grass Top - zgoraj zeleno, stranice zemlja
|
||||
darkColor = 0x8b6f47; // Dirt color (brown)
|
||||
darkerColor = 0x5d4a2e; // Darker Dirt
|
||||
} else if (type.name === 'dirt') {
|
||||
// C: Full Dirt - vse rjavo
|
||||
darkColor = 0x8b6f47; // Dirt
|
||||
darkerColor = 0x654321; // Darker Dirt
|
||||
} else {
|
||||
// Standard block: Darken base color significantly
|
||||
darkColor = Phaser.Display.Color.IntegerToColor(type.color).darken(30).color;
|
||||
darkerColor = Phaser.Display.Color.IntegerToColor(type.color).darken(50).color;
|
||||
graphics.fillStyle(type.color);
|
||||
|
||||
// Diamond Only
|
||||
graphics.beginPath();
|
||||
graphics.moveTo(midX, top);
|
||||
graphics.lineTo(x + 48, midY);
|
||||
graphics.lineTo(midX, bottomY);
|
||||
graphics.lineTo(x, midY);
|
||||
graphics.closePath();
|
||||
graphics.fill();
|
||||
|
||||
// Grid Stroke (Black/Dark)
|
||||
graphics.lineStyle(1, 0x000000, 0.3);
|
||||
graphics.strokePath();
|
||||
|
||||
// Simple details
|
||||
if (type.name.includes('grass')) {
|
||||
graphics.fillStyle(0x339933);
|
||||
for (let i = 0; i < 5; i++) {
|
||||
const rx = x + 10 + Math.random() * 28;
|
||||
const ry = 5 + Math.random() * 14;
|
||||
graphics.fillRect(rx, ry, 2, 2);
|
||||
}
|
||||
}
|
||||
|
||||
// 1. Draw LEFT Side (Darker) - Minecraft volumetric effect
|
||||
graphics.fillStyle(darkColor, 1);
|
||||
graphics.beginPath();
|
||||
graphics.moveTo(0, tileH / 2); // Left Corner
|
||||
graphics.lineTo(tileW / 2, tileH); // Bottom Corner
|
||||
graphics.lineTo(tileW / 2, tileH + thickness); // Bottom Corner (Lowered)
|
||||
graphics.lineTo(0, tileH / 2 + thickness); // Left Corner (Lowered)
|
||||
graphics.closePath();
|
||||
graphics.fillPath();
|
||||
|
||||
// 2. Draw RIGHT Side (Darkest) - Strong shadow
|
||||
graphics.fillStyle(darkerColor, 1);
|
||||
graphics.beginPath();
|
||||
graphics.moveTo(tileW / 2, tileH); // Bottom Corner
|
||||
graphics.lineTo(tileW, tileH / 2); // Right Corner
|
||||
graphics.lineTo(tileW, tileH / 2 + thickness); // Right Corner (Lowered)
|
||||
graphics.lineTo(tileW / 2, tileH + thickness); // Bottom Corner (Lowered)
|
||||
graphics.closePath();
|
||||
graphics.fillPath();
|
||||
|
||||
// 3. Draw TOP Surface (bright)
|
||||
graphics.fillStyle(type.color, 1);
|
||||
graphics.beginPath();
|
||||
graphics.moveTo(tileW / 2, 0); // Top
|
||||
graphics.lineTo(tileW, tileH / 2); // Right
|
||||
graphics.lineTo(tileW / 2, tileH); // Bottom
|
||||
graphics.lineTo(0, tileH / 2); // Left
|
||||
graphics.closePath();
|
||||
graphics.fillPath();
|
||||
|
||||
// Generate texture
|
||||
graphics.generateTexture(key, tileW, tileH + thickness);
|
||||
graphics.generateTexture(type.name, tileWidth, tileHeight);
|
||||
graphics.destroy();
|
||||
|
||||
// Update texture name in type def
|
||||
type.texture = key;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
createGravestoneSprite() {
|
||||
const canvas = document.createElement('canvas');
|
||||
const size = 32;
|
||||
canvas.width = size;
|
||||
canvas.height = size;
|
||||
const ctx = canvas.getContext('2d', { willReadFrequently: true });
|
||||
|
||||
if (this.scene.textures.exists('objects_pack')) {
|
||||
const sourceTexture = this.scene.textures.get('objects_pack');
|
||||
const sourceImg = sourceTexture.getSourceImage();
|
||||
const sourceX = 240;
|
||||
const sourceY = 160;
|
||||
ctx.drawImage(sourceImg, sourceX, sourceY, size, size, 0, 0, size, size);
|
||||
this.scene.textures.addCanvas('gravestone', canvas);
|
||||
console.log('✅ Gravestone sprite extracted!');
|
||||
}
|
||||
}
|
||||
|
||||
// Generiraj teren (data only)
|
||||
generate() {
|
||||
console.log(`🌍 Generating terrain data: ${this.width}x${this.height}...`);
|
||||
|
||||
// Zagotovi teksture
|
||||
this.createTileTextures();
|
||||
|
||||
if (!this.scene.textures.exists('flower')) TextureGenerator.createFlowerSprite(this.scene, 'flower');
|
||||
if (this.scene.textures.exists('stone_sprite')) {
|
||||
if (!this.scene.textures.exists('bush')) this.scene.textures.addImage('bush', this.scene.textures.get('stone_sprite').getSourceImage());
|
||||
} else if (!this.scene.textures.exists('bush')) {
|
||||
TextureGenerator.createBushSprite(this.scene, 'bush');
|
||||
}
|
||||
|
||||
if (this.scene.textures.exists('tree_sprite')) {
|
||||
if (!this.scene.textures.exists('tree')) this.scene.textures.addImage('tree', this.scene.textures.get('tree_sprite').getSourceImage());
|
||||
} else if (!this.scene.textures.exists('tree')) {
|
||||
TextureGenerator.createTreeSprite(this.scene, 'tree');
|
||||
}
|
||||
|
||||
if (this.scene.textures.exists('objects_pack') && !this.scene.textures.exists('gravestone')) {
|
||||
this.createGravestoneSprite();
|
||||
}
|
||||
|
||||
for (let i = 1; i <= 4; i++) {
|
||||
if (!this.scene.textures.exists(`crop_stage_${i}`)) TextureGenerator.createCropSprite(this.scene, `crop_stage_${i}`, i);
|
||||
}
|
||||
|
||||
this.decorationsMap.clear();
|
||||
this.decorations = [];
|
||||
this.cropsMap.clear();
|
||||
|
||||
for (let y = 0; y < this.height; y++) {
|
||||
this.tiles[y] = [];
|
||||
for (let x = 0; x < this.width; x++) {
|
||||
const noiseValue = this.noise.getNormalized(x, y, 0.05, 4);
|
||||
let elevation = this.noise.getNormalized(x, y, 0.03, 3);
|
||||
let terrainType = this.getTerrainTypeByElevation(noiseValue, elevation);
|
||||
const nx = x * 0.1;
|
||||
const ny = y * 0.1;
|
||||
const elevation = this.noise.noise(nx, ny);
|
||||
|
||||
const pathNoise = this.noise.getNormalized(x, y, 0.08, 2);
|
||||
if (pathNoise > 0.48 && pathNoise < 0.52 && noiseValue > 0.35) {
|
||||
terrainType = this.terrainTypes.PATH;
|
||||
}
|
||||
|
||||
const edgeDistance = 2;
|
||||
const isNearEdge = x < edgeDistance || x >= this.width - edgeDistance || y < edgeDistance || y >= this.height - edgeDistance;
|
||||
const isEdge = x === 0 || x === this.width - 1 || y === 0 || y === this.height - 1;
|
||||
|
||||
if (isEdge) terrainType = this.terrainTypes.STONE;
|
||||
|
||||
if (isEdge) elevation = 0;
|
||||
else if (isNearEdge) elevation = Math.max(0, elevation - 0.2);
|
||||
let terrainType = this.terrainTypes.WATER;
|
||||
if (elevation > this.terrainTypes.SAND.height) terrainType = this.terrainTypes.SAND;
|
||||
if (elevation > this.terrainTypes.GRASS_FULL.height) terrainType = this.terrainTypes.GRASS_FULL;
|
||||
if (elevation > this.terrainTypes.DIRT.height) terrainType = this.terrainTypes.DIRT;
|
||||
if (elevation > this.terrainTypes.STONE.height) terrainType = this.terrainTypes.STONE;
|
||||
|
||||
this.tiles[y][x] = {
|
||||
gridX: x,
|
||||
gridY: y,
|
||||
type: terrainType.name,
|
||||
texture: terrainType.texture,
|
||||
height: noiseValue,
|
||||
elevation: elevation,
|
||||
yLayer: terrainType.yLayer,
|
||||
texture: terrainType.name,
|
||||
hasDecoration: false,
|
||||
hasCrop: false
|
||||
};
|
||||
@@ -291,24 +165,33 @@ class TerrainSystem {
|
||||
|
||||
if (terrainType.name.includes('grass')) {
|
||||
const rand = Math.random();
|
||||
if (elevation > 0.6 && rand < 0.05) {
|
||||
if (elevation > 0.6 && rand < 0.1) {
|
||||
decorType = 'bush';
|
||||
maxHp = 5;
|
||||
} else if (rand < 0.025) {
|
||||
}
|
||||
// Trees - Volumetric
|
||||
else if (rand < 0.15) {
|
||||
decorType = 'tree';
|
||||
maxHp = 5;
|
||||
const sizeRand = Math.random();
|
||||
if (sizeRand < 0.2) scale = 0.25;
|
||||
else if (sizeRand < 0.8) scale = 0.6 + Math.random() * 0.2;
|
||||
else scale = 1.0;
|
||||
} else if (rand < 0.03) {
|
||||
if (sizeRand < 0.2) scale = 0.8;
|
||||
else if (sizeRand < 0.8) scale = 1.0 + Math.random() * 0.3;
|
||||
else scale = 1.3;
|
||||
}
|
||||
// Rocks - Volumetric
|
||||
else if (rand < 0.18) {
|
||||
decorType = 'rock'; // 'rock' texture from TextureGenerator
|
||||
maxHp = 8;
|
||||
scale = 1.5; // Big rocks
|
||||
}
|
||||
else if (rand < 0.19) {
|
||||
decorType = 'gravestone';
|
||||
maxHp = 10;
|
||||
} else if (rand < 0.08) {
|
||||
} else if (rand < 0.30) {
|
||||
decorType = 'flower';
|
||||
maxHp = 1;
|
||||
}
|
||||
} else if (terrainType.name === 'dirt' && Math.random() < 0.02) {
|
||||
} else if (terrainType.name === 'dirt' && Math.random() < 0.05) {
|
||||
decorType = 'bush';
|
||||
maxHp = 3;
|
||||
}
|
||||
@@ -401,14 +284,12 @@ class TerrainSystem {
|
||||
|
||||
setTileType(x, y, typeName) {
|
||||
if (!this.tiles[y] || !this.tiles[y][x]) return;
|
||||
const typeDef = Object.values(this.terrainTypes).find(t => t.name === typeName);
|
||||
if (!typeDef) return;
|
||||
this.tiles[y][x].type = typeName;
|
||||
this.tiles[y][x].texture = typeDef.texture;
|
||||
|
||||
const key = `${x},${y}`;
|
||||
if (this.visibleTiles.has(key)) {
|
||||
const sprite = this.visibleTiles.get(key);
|
||||
sprite.setTexture(typeDef.texture);
|
||||
sprite.setTexture(typeName);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -446,8 +327,14 @@ class TerrainSystem {
|
||||
this.offsetY = offsetY;
|
||||
}
|
||||
|
||||
getTile(x, y) {
|
||||
if (this.tiles[y] && this.tiles[y][x]) {
|
||||
return this.tiles[y][x];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
updateCulling(camera) {
|
||||
// Simple Culling
|
||||
const view = camera.worldView;
|
||||
let buffer = 200;
|
||||
if (this.scene.settings && this.scene.settings.viewDistance === 'LOW') buffer = 50;
|
||||
@@ -471,25 +358,26 @@ class TerrainSystem {
|
||||
const startY = Math.max(0, minGridY);
|
||||
const endY = Math.min(this.height, maxGridY);
|
||||
|
||||
const neededKeys = new Set();
|
||||
const neededTileKeys = new Set();
|
||||
const neededDecorKeys = new Set();
|
||||
const neededCropKeys = new Set();
|
||||
|
||||
for (let y = startY; y < endY; y++) {
|
||||
for (let x = startX; x < endX; x++) {
|
||||
// TILES
|
||||
const key = `${x},${y}`;
|
||||
neededKeys.add(key);
|
||||
const tile = this.tiles[y][x];
|
||||
|
||||
if (!this.visibleTiles.has(key)) {
|
||||
const tile = this.tiles[y][x];
|
||||
const screenPos = this.iso.toScreen(x, y);
|
||||
const sprite = this.tilePool.get();
|
||||
|
||||
sprite.setPosition(screenPos.x + this.offsetX, screenPos.y + this.offsetY);
|
||||
sprite.setTexture(tile.texture);
|
||||
sprite.setDepth(this.iso.getDepth(x, y));
|
||||
this.visibleTiles.set(key, sprite);
|
||||
// TILES
|
||||
if (tile) {
|
||||
neededTileKeys.add(key);
|
||||
if (!this.visibleTiles.has(key)) {
|
||||
const sprite = this.tilePool.get();
|
||||
sprite.setTexture(tile.type);
|
||||
const screenPos = this.iso.toScreen(x, y);
|
||||
sprite.setPosition(screenPos.x + this.offsetX, screenPos.y + this.offsetY);
|
||||
sprite.setDepth(this.iso.getDepth(x, y));
|
||||
this.visibleTiles.set(key, sprite);
|
||||
}
|
||||
}
|
||||
|
||||
// DECORATIONS
|
||||
@@ -501,23 +389,19 @@ class TerrainSystem {
|
||||
const screenPos = this.iso.toScreen(x, y);
|
||||
|
||||
sprite.setPosition(screenPos.x + this.offsetX, screenPos.y + this.offsetY);
|
||||
// Fix for house sprite
|
||||
if (decor.type.includes('house_sprite') || decor.type.includes('market_sprite')) {
|
||||
sprite.setTexture(decor.type);
|
||||
sprite.setScale(decor.scale || 1.0);
|
||||
|
||||
if (decor.type.includes('house_sprite') || decor.type.includes('market') || decor.type.includes('structure')) {
|
||||
sprite.setOrigin(0.5, 0.8);
|
||||
} else {
|
||||
sprite.setTexture(decor.type);
|
||||
sprite.setScale(decor.scale || 1);
|
||||
// Volumetric trees/rocks origin adjustment
|
||||
// Usually volumetric needed (0.5, 1) or slightly different?
|
||||
sprite.setOrigin(0.5, 0.9); // Slight tweak
|
||||
}
|
||||
|
||||
// Origin adjusted for buildings
|
||||
if (decor.type.includes('house') || decor.type.includes('market')) {
|
||||
sprite.setOrigin(0.5, 0.8); // Adjusted origin for buildings
|
||||
} else {
|
||||
sprite.setOrigin(0.5, 1);
|
||||
}
|
||||
sprite.setTexture(decor.type);
|
||||
sprite.setScale(decor.scale || 1.0);
|
||||
|
||||
sprite.setDepth(this.iso.getDepth(x, y) + 1); // Above tile
|
||||
sprite.setDepth(this.iso.getDepth(x, y) + 1);
|
||||
this.visibleDecorations.set(key, sprite);
|
||||
}
|
||||
}
|
||||
@@ -538,16 +422,14 @@ class TerrainSystem {
|
||||
}
|
||||
}
|
||||
|
||||
// Cleanup tiles
|
||||
// Cleanup
|
||||
for (const [key, sprite] of this.visibleTiles) {
|
||||
if (!neededKeys.has(key)) {
|
||||
if (!neededTileKeys.has(key)) {
|
||||
sprite.setVisible(false);
|
||||
this.tilePool.release(sprite);
|
||||
this.visibleTiles.delete(key);
|
||||
}
|
||||
}
|
||||
|
||||
// Cleanup decorations
|
||||
for (const [key, sprite] of this.visibleDecorations) {
|
||||
if (!neededDecorKeys.has(key)) {
|
||||
sprite.setVisible(false);
|
||||
@@ -555,8 +437,6 @@ class TerrainSystem {
|
||||
this.visibleDecorations.delete(key);
|
||||
}
|
||||
}
|
||||
|
||||
// Cleanup crops
|
||||
for (const [key, sprite] of this.visibleCrops) {
|
||||
if (!neededCropKeys.has(key)) {
|
||||
sprite.setVisible(false);
|
||||
|
||||
Reference in New Issue
Block a user