udomacenje zombija in uboj\
This commit is contained in:
@@ -18,6 +18,7 @@ class FarmingSystem {
|
||||
// Let's say if it has crop and it is ripe, harvest it regardless of tool.
|
||||
if (tile.hasCrop) {
|
||||
const crop = terrain.cropsMap.get(`${gridX},${gridY}`);
|
||||
console.log('🌾 Check harvest:', crop);
|
||||
if (crop && crop.stage === 4) {
|
||||
this.harvest(gridX, gridY);
|
||||
return true;
|
||||
@@ -26,10 +27,11 @@ class FarmingSystem {
|
||||
|
||||
// 2. TILLING (Requires Hoe)
|
||||
if (toolType === 'hoe') {
|
||||
if (tile.type === 'grass' || tile.type === 'dirt') {
|
||||
const typeName = tile.type.name || tile.type;
|
||||
if (typeName.includes('grass') || typeName === 'dirt') {
|
||||
if (!tile.hasDecoration && !tile.hasCrop) {
|
||||
console.log('🚜 Tilling soil...');
|
||||
terrain.setTileType(gridX, gridY, 'farmland');
|
||||
terrain.setTileType(gridX, gridY, 'farmland'); // This sets it to string 'farmland' usually? or object? Assuming method handles it.
|
||||
// Play sound
|
||||
return true;
|
||||
}
|
||||
@@ -38,10 +40,16 @@ class FarmingSystem {
|
||||
|
||||
// 3. PLANTING (Requires Seeds)
|
||||
if (toolType === 'seeds') {
|
||||
if (tile.type === 'farmland' && !tile.hasCrop && !tile.hasDecoration) {
|
||||
const typeName = tile.type.name || tile.type;
|
||||
if (typeName === 'farmland' && !tile.hasCrop && !tile.hasDecoration) {
|
||||
console.log('🌱 Planting seeds...');
|
||||
this.plant(gridX, gridY);
|
||||
return true; // Consume seed logic handled by caller?
|
||||
|
||||
// Remove 1 seed from inventory
|
||||
if (this.scene.inventorySystem) {
|
||||
this.scene.inventorySystem.removeItem('seeds', 1);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,315 +3,228 @@ class InteractionSystem {
|
||||
this.scene = scene;
|
||||
this.iso = new IsometricUtils(48, 24);
|
||||
|
||||
// Input listener setup (only once)
|
||||
// Input listener setup
|
||||
this.scene.input.on('pointerdown', (pointer) => {
|
||||
if (pointer.button === 0) { // Left Click
|
||||
this.handleLeftClick(pointer);
|
||||
const worldX = pointer.worldX - this.scene.terrainOffsetX;
|
||||
const worldY = pointer.worldY - this.scene.terrainOffsetY;
|
||||
const gridPos = this.iso.toGrid(worldX, worldY);
|
||||
this.handleInteraction(gridPos.x, gridPos.y, false);
|
||||
}
|
||||
});
|
||||
|
||||
// Key E listener (Easy Interaction/Tame)
|
||||
this.scene.input.keyboard.on('keydown-E', () => {
|
||||
this.handleInteractKey();
|
||||
});
|
||||
|
||||
// Loot Array
|
||||
this.drops = [];
|
||||
}
|
||||
|
||||
handleLeftClick(pointer) {
|
||||
if (!this.scene.player) return;
|
||||
|
||||
// 1. Account for camera and offset
|
||||
const worldX = pointer.worldX - this.scene.terrainOffsetX;
|
||||
const worldY = pointer.worldY - this.scene.terrainOffsetY;
|
||||
|
||||
// 2. Convert to Grid
|
||||
const gridPos = this.iso.toGrid(worldX, worldY);
|
||||
|
||||
// 3. Check distance
|
||||
handleInteractKey() {
|
||||
if (!this.scene.player || !this.scene.npcs) return;
|
||||
const playerPos = this.scene.player.getPosition();
|
||||
const dist = Phaser.Math.Distance.Between(playerPos.x, playerPos.y, gridPos.x, gridPos.y);
|
||||
|
||||
// Allow interaction within radius of 2.5 tiles
|
||||
if (dist > 2.5) {
|
||||
console.log('Too far:', dist.toFixed(1));
|
||||
return;
|
||||
}
|
||||
// Find nearest NPC
|
||||
let nearest = null;
|
||||
let minDist = 2.5; // Interaction range
|
||||
|
||||
console.log(`☝️ Clicked tile: ${gridPos.x},${gridPos.y}`);
|
||||
|
||||
// DETERMINE TOOL / ACTION
|
||||
let activeTool = null;
|
||||
const uiScene = this.scene.scene.get('UIScene');
|
||||
const invSys = this.scene.inventorySystem;
|
||||
|
||||
if (uiScene && invSys) {
|
||||
const selectedIdx = uiScene.selectedSlot;
|
||||
const slotData = invSys.slots[selectedIdx];
|
||||
if (slotData) activeTool = slotData.type;
|
||||
}
|
||||
|
||||
// 0. Build Mode Override
|
||||
if (this.scene.buildingSystem && this.scene.buildingSystem.isBuildMode) {
|
||||
this.scene.buildingSystem.tryBuild(gridPos.x, gridPos.y);
|
||||
return; // Consume click
|
||||
}
|
||||
|
||||
// 3.5 Check for NPC Click
|
||||
if (this.scene.npcs) {
|
||||
for (const npc of this.scene.npcs) {
|
||||
if (Math.abs(npc.gridX - gridPos.x) < 2.5 && Math.abs(npc.gridY - gridPos.y) < 2.5) {
|
||||
console.log(`🗣️ Interact with NPC: ${npc.type}`);
|
||||
|
||||
if (npc.type === 'zombie') {
|
||||
// Taming Logic
|
||||
npc.toggleState();
|
||||
return; // Done
|
||||
}
|
||||
|
||||
if (npc.type === 'merchant') {
|
||||
// Open Trade Menu
|
||||
if (uiScene && invSys) {
|
||||
uiScene.showTradeMenu(invSys);
|
||||
}
|
||||
return; // Stop processing
|
||||
}
|
||||
return; // Stop processing other clicks (farming/terrain) if clicked NPC
|
||||
}
|
||||
for (const npc of this.scene.npcs) {
|
||||
const d = Phaser.Math.Distance.Between(playerPos.x, playerPos.y, npc.gridX, npc.gridY);
|
||||
if (d < minDist) {
|
||||
minDist = d;
|
||||
nearest = npc;
|
||||
}
|
||||
}
|
||||
|
||||
// 4. Try Farming Action (Tilling, Planting, Harvesting)
|
||||
if (this.scene.farmingSystem) {
|
||||
const didFarm = this.scene.farmingSystem.interact(gridPos.x, gridPos.y, activeTool);
|
||||
if (didFarm) {
|
||||
// Animation?
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 5. Try damage decoration
|
||||
const id = `${gridPos.x},${gridPos.y}`;
|
||||
if (this.scene.terrainSystem.decorationsMap.has(id)) {
|
||||
const decor = this.scene.terrainSystem.decorationsMap.get(id);
|
||||
|
||||
// Calculate Damage based on Tool
|
||||
let damage = 1; // Default hand damage
|
||||
|
||||
// Tool Logic
|
||||
if (decor.type === 'tree' && activeTool === 'axe') {
|
||||
damage = 3; // Axe destroys tree fast
|
||||
} else if ((decor.type === 'bush' || decor.type === 'stone') && activeTool === 'pickaxe') {
|
||||
damage = 3; // Pickaxe destroys stone fast
|
||||
}
|
||||
|
||||
// Apply damage
|
||||
const result = this.scene.terrainSystem.damageDecoration(gridPos.x, gridPos.y, damage);
|
||||
|
||||
if (result === 'destroyed') {
|
||||
// Play proper sound
|
||||
if (decor.type === 'tree') {
|
||||
if (this.scene.soundManager) this.scene.soundManager.playChop();
|
||||
} else {
|
||||
// Play stone break sound (using chop for now or generic hit)
|
||||
if (this.scene.soundManager) this.scene.soundManager.playChop();
|
||||
}
|
||||
|
||||
// AUTO-LOOT directly to Inventory
|
||||
let lootType = 'wood'; // Default
|
||||
let lootCount = 1;
|
||||
|
||||
if (decor.type === 'tree') {
|
||||
lootType = 'wood';
|
||||
lootCount = 3 + Math.floor(Math.random() * 3); // 3-5 wood
|
||||
} else if (decor.type === 'bush' || decor.type === 'stone') {
|
||||
lootType = 'stone';
|
||||
lootCount = 2 + Math.floor(Math.random() * 3); // 2-4 stone
|
||||
} else if (decor.type === 'flower') {
|
||||
lootType = 'seeds'; // Flowers drop seeds? Or flower item?
|
||||
lootCount = 1;
|
||||
}
|
||||
|
||||
console.log(`🎁 Auto-looted: ${lootCount}x ${lootType}`);
|
||||
|
||||
if (invSys) {
|
||||
invSys.addItem(lootType, lootCount);
|
||||
|
||||
// Show floating text feedback " +3 Wood "
|
||||
const screenPos = this.iso.toScreen(gridPos.x, gridPos.y);
|
||||
const txt = this.scene.add.text(
|
||||
screenPos.x + this.scene.terrainOffsetX,
|
||||
screenPos.y + this.scene.terrainOffsetY - 30,
|
||||
`+${lootCount} ${lootType.toUpperCase()}`,
|
||||
{ fontSize: '14px', fill: '#ffff00', stroke: '#000', strokeThickness: 2 }
|
||||
);
|
||||
txt.setOrigin(0.5);
|
||||
this.scene.tweens.add({
|
||||
targets: txt,
|
||||
y: txt.y - 40,
|
||||
alpha: 0,
|
||||
duration: 1000,
|
||||
onComplete: () => txt.destroy()
|
||||
});
|
||||
}
|
||||
|
||||
} else if (result === 'hit') {
|
||||
// Play hit sound
|
||||
if (this.scene.soundManager) this.scene.soundManager.playChop();
|
||||
if (nearest) {
|
||||
console.log('E Interacted with:', nearest.type);
|
||||
if (nearest.type === 'zombie') {
|
||||
// Always Tame on E key (Combat is Space/Click)
|
||||
nearest.tame();
|
||||
} else {
|
||||
nearest.toggleState(); // Merchant/NPC talk
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
handleDecorationClick(gridX, gridY) {
|
||||
handleInteraction(gridX, gridY, isAttack = false) {
|
||||
if (!this.scene.player) return;
|
||||
|
||||
// Check distance
|
||||
// 3. Check distance
|
||||
const playerPos = this.scene.player.getPosition();
|
||||
const dist = Phaser.Math.Distance.Between(playerPos.x, playerPos.y, gridX, gridY);
|
||||
|
||||
if (dist > 3.0) { // Slightly increased radius for easier clicking
|
||||
console.log('Too far:', dist.toFixed(1));
|
||||
return;
|
||||
}
|
||||
// Allow interaction within radius of 2.5 tiles (1.5 for attack)
|
||||
const maxDist = isAttack ? 1.5 : 2.5;
|
||||
if (dist > maxDist) return;
|
||||
|
||||
// Get Active Tool
|
||||
let activeTool = null;
|
||||
// DETERMINE TOOL
|
||||
let activeTool = isAttack ? 'sword' : null;
|
||||
const uiScene = this.scene.scene.get('UIScene');
|
||||
const invSys = this.scene.inventorySystem;
|
||||
|
||||
if (uiScene && invSys) {
|
||||
if (uiScene && invSys && !isAttack) {
|
||||
const selectedIdx = uiScene.selectedSlot;
|
||||
const slotData = invSys.slots[selectedIdx];
|
||||
if (slotData) activeTool = slotData.type;
|
||||
}
|
||||
|
||||
// REUSE LOGIC
|
||||
if (isAttack && invSys) {
|
||||
const selectedIdx = uiScene.selectedSlot;
|
||||
const slotData = invSys.slots[selectedIdx];
|
||||
if (slotData) activeTool = slotData.type;
|
||||
}
|
||||
|
||||
// 0. Build Mode Override (Only click)
|
||||
if (!isAttack && this.scene.buildingSystem && this.scene.buildingSystem.isBuildMode) {
|
||||
this.scene.buildingSystem.tryBuild(gridX, gridY);
|
||||
return;
|
||||
}
|
||||
|
||||
// 3.5 Check for NPC Interaction
|
||||
if (this.scene.npcs) {
|
||||
for (const npc of this.scene.npcs) {
|
||||
// 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) {
|
||||
|
||||
if (npc.type === 'merchant' && !isAttack) {
|
||||
if (uiScene && invSys) uiScene.showTradeMenu(invSys);
|
||||
return;
|
||||
}
|
||||
|
||||
if (npc.type === 'zombie') {
|
||||
// Logic: Attack vs Tame
|
||||
const isWeapon = activeTool === 'sword' || activeTool === 'axe' || activeTool === 'pickaxe';
|
||||
|
||||
if (isAttack || isWeapon) {
|
||||
// COMBAT
|
||||
let damage = 1;
|
||||
if (activeTool === 'sword') damage = 5;
|
||||
if (activeTool === 'axe') damage = 3;
|
||||
if (activeTool === 'pickaxe') damage = 2;
|
||||
|
||||
if (npc.takeDamage) {
|
||||
npc.takeDamage(damage);
|
||||
}
|
||||
return;
|
||||
}
|
||||
else {
|
||||
// TAME ATTEMPT
|
||||
console.log('🤝 Attempting to TAME zombie at', npc.gridX, npc.gridY);
|
||||
npc.tame();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isAttack) npc.toggleState();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 4. Try Farming Action
|
||||
if (this.scene.farmingSystem && !isAttack) {
|
||||
const didFarm = this.scene.farmingSystem.interact(gridX, gridY, activeTool);
|
||||
if (didFarm) return;
|
||||
}
|
||||
|
||||
// 5. Try damage decoration
|
||||
const id = `${gridX},${gridY}`;
|
||||
if (this.scene.terrainSystem.decorationsMap.has(id)) {
|
||||
const decor = this.scene.terrainSystem.decorationsMap.get(id);
|
||||
|
||||
// Calculate Damage based on Tool
|
||||
let damage = 1; // Default hand damage
|
||||
let damage = 1;
|
||||
|
||||
// Tool Logic
|
||||
if (decor.type === 'tree' && activeTool === 'axe') {
|
||||
damage = 3; // Axe destroys tree fast
|
||||
} else if ((decor.type === 'bush' || decor.type === 'stone') && activeTool === 'pickaxe') {
|
||||
damage = 3; // Pickaxe destroys stone fast
|
||||
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;
|
||||
|
||||
// Apply damage
|
||||
const result = this.scene.terrainSystem.damageDecoration(gridX, gridY, damage);
|
||||
decor.hp -= damage;
|
||||
this.showFloatingText(`${-damage}`, gridX, gridY, '#ffaaaa');
|
||||
|
||||
if (result === 'destroyed') {
|
||||
// Play proper sound
|
||||
if (decor.type === 'tree') {
|
||||
if (this.scene.soundManager) this.scene.soundManager.playChop();
|
||||
} else {
|
||||
if (this.scene.soundManager) this.scene.soundManager.playChop();
|
||||
if (decor.hp <= 0) {
|
||||
const type = this.scene.terrainSystem.removeDecoration(gridX, gridY);
|
||||
// Loot logic
|
||||
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);
|
||||
}
|
||||
|
||||
// AUTO-LOOT directly to Inventory
|
||||
let lootType = 'wood'; // Default
|
||||
let lootCount = 1;
|
||||
|
||||
if (decor.type === 'tree') {
|
||||
lootType = 'wood';
|
||||
lootCount = 3 + Math.floor(Math.random() * 3); // 3-5 wood
|
||||
} else if (decor.type === 'bush' || decor.type === 'stone') {
|
||||
lootType = 'stone';
|
||||
lootCount = 2 + Math.floor(Math.random() * 3); // 2-4 stone
|
||||
} else if (decor.type === 'flower') {
|
||||
lootType = 'seeds';
|
||||
lootCount = 1;
|
||||
}
|
||||
|
||||
console.log(`🎁 Auto-looted: ${lootCount}x ${lootType}`);
|
||||
|
||||
if (invSys) {
|
||||
invSys.addItem(lootType, lootCount);
|
||||
|
||||
// Show floating text feedback
|
||||
const screenPos = this.iso.toScreen(gridX, gridY);
|
||||
const txt = this.scene.add.text(
|
||||
screenPos.x + this.scene.terrainOffsetX,
|
||||
screenPos.y + this.scene.terrainOffsetY - 30,
|
||||
`+${lootCount} ${lootType.toUpperCase()}`,
|
||||
{ fontSize: '14px', fill: '#ffff00', stroke: '#000', strokeThickness: 2 }
|
||||
);
|
||||
txt.setOrigin(0.5);
|
||||
} else {
|
||||
// Shake visual
|
||||
const sprite = this.scene.terrainSystem.visibleDecorations.get(id);
|
||||
if (sprite) {
|
||||
this.scene.tweens.add({
|
||||
targets: txt,
|
||||
y: txt.y - 40,
|
||||
alpha: 0,
|
||||
duration: 1000,
|
||||
onComplete: () => txt.destroy()
|
||||
targets: sprite,
|
||||
x: sprite.x + 2, yoyo: true, duration: 50, repeat: 2
|
||||
});
|
||||
sprite.setTint(0xffaaaa);
|
||||
this.scene.time.delayedCall(200, () => sprite.clearTint());
|
||||
}
|
||||
|
||||
} else if (result === 'hit') {
|
||||
if (this.scene.soundManager) this.scene.soundManager.playChop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
showFloatingText(text, gridX, gridY, color) {
|
||||
const screenPos = this.iso.toScreen(gridX, gridY);
|
||||
const txt = this.scene.add.text(
|
||||
screenPos.x + this.scene.terrainOffsetX,
|
||||
screenPos.y + this.scene.terrainOffsetY - 30,
|
||||
text,
|
||||
{ fontSize: '14px', fill: color, stroke: '#000', strokeThickness: 2 }
|
||||
).setOrigin(0.5);
|
||||
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}`);
|
||||
|
||||
// Convert to Screen
|
||||
const screenPos = this.iso.toScreen(gridX, gridY);
|
||||
const x = screenPos.x + this.scene.terrainOffsetX;
|
||||
const y = screenPos.y + this.scene.terrainOffsetY;
|
||||
|
||||
// Create simplistic item drop sprite
|
||||
let symbol = '?';
|
||||
if (type === 'wood') symbol = '🪵';
|
||||
if (type === 'stone') symbol = '🪨';
|
||||
if (type === 'seeds') symbol = '🌱';
|
||||
if (type === 'wheat') symbol = '🌾';
|
||||
if (type === 'hoe') 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); // above tiles
|
||||
drop.setDepth(this.iso.getDepth(gridX, gridY) + 500);
|
||||
|
||||
// Bounce animation
|
||||
this.scene.tweens.add({
|
||||
targets: drop,
|
||||
y: y - 40,
|
||||
duration: 500,
|
||||
yoyo: true,
|
||||
ease: 'Sine.easeOut',
|
||||
repeat: -1
|
||||
targets: drop, y: y - 40, duration: 500, yoyo: true, ease: 'Sine.easeOut', repeat: -1
|
||||
});
|
||||
|
||||
this.drops.push({
|
||||
gridX,
|
||||
gridY,
|
||||
sprite: drop,
|
||||
type: type
|
||||
});
|
||||
this.drops.push({ gridX, gridY, sprite: drop, type: type });
|
||||
}
|
||||
|
||||
update() {
|
||||
// Check for player pickup
|
||||
if (!this.scene.player) return;
|
||||
|
||||
const playerPos = this.scene.player.getPosition();
|
||||
|
||||
// Filter drops to pick up
|
||||
for (let i = this.drops.length - 1; i >= 0; i--) {
|
||||
const drop = this.drops[i];
|
||||
|
||||
// Check if player is ON the drop tile
|
||||
if (Math.abs(drop.gridX - playerPos.x) < 0.8 && Math.abs(drop.gridY - playerPos.y) < 0.8) {
|
||||
// Pick up!
|
||||
console.log('🎒 Picked up:', drop.type);
|
||||
|
||||
// Play pickup sound
|
||||
if (this.scene.soundManager) this.scene.soundManager.playPickup();
|
||||
|
||||
// Add to inventory
|
||||
if (this.scene.inventorySystem) {
|
||||
this.scene.inventorySystem.addItem(drop.type, 1);
|
||||
}
|
||||
|
||||
// Destroy visual
|
||||
if (this.scene.inventorySystem) this.scene.inventorySystem.addItem(drop.type, 1);
|
||||
drop.sprite.destroy();
|
||||
this.drops.splice(i, 1);
|
||||
}
|
||||
|
||||
@@ -13,7 +13,6 @@ class TerrainSystem {
|
||||
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
|
||||
@@ -22,6 +21,13 @@ class TerrainSystem {
|
||||
this.offsetX = 0;
|
||||
this.offsetY = 0;
|
||||
|
||||
// Culling optimization
|
||||
this.lastCullX = -9999;
|
||||
this.lastCullY = -9999;
|
||||
|
||||
// Object Pools
|
||||
// Tiles will use Blitter, so no Sprite Pool needed for them.
|
||||
this.blitters = new Map(); // Key: textureKey, Value: Blitter Object
|
||||
// Object Pools
|
||||
this.tilePool = new ObjectPool(
|
||||
() => {
|
||||
@@ -251,6 +257,8 @@ class TerrainSystem {
|
||||
// Zagotovi teksture
|
||||
this.createTileTextures();
|
||||
|
||||
// DELETED Blitter Init
|
||||
|
||||
// Zagotovi decoration teksture - check for custom sprites first
|
||||
if (!this.scene.textures.exists('flower')) {
|
||||
TextureGenerator.createFlowerSprite(this.scene, 'flower');
|
||||
@@ -496,7 +504,7 @@ class TerrainSystem {
|
||||
|
||||
// Force Visual Update immediately?
|
||||
// updateCulling will catch it on next frame, but to be safe:
|
||||
// Or leave it to update loop.
|
||||
this.lastCullX = -9999; // Force update
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -524,7 +532,9 @@ class TerrainSystem {
|
||||
const key = `${x},${y}`;
|
||||
this.cropsMap.set(key, cropData);
|
||||
this.tiles[y][x].hasCrop = true;
|
||||
// updateCulling loop will pick it up on next frame
|
||||
this.tiles[y][x].hasCrop = true;
|
||||
// updateCulling loop will pick it up on next frame but we force it
|
||||
this.lastCullX = -9999;
|
||||
}
|
||||
|
||||
removeCrop(x, y) {
|
||||
@@ -558,14 +568,32 @@ class TerrainSystem {
|
||||
|
||||
// Update culling (called every frame)
|
||||
updateCulling(camera) {
|
||||
// Throttling Optimization - TEMPORARILY DISABLED FOR DEBUG
|
||||
// const dist = Phaser.Math.Distance.Between(camera.scrollX, camera.scrollY, this.lastCullX, this.lastCullY);
|
||||
// if (dist < 50) return;
|
||||
|
||||
// Debug log once
|
||||
if (!this.hasLogged) {
|
||||
console.log('UpdateCulling running. Camera:', camera.scrollX, camera.scrollY);
|
||||
this.hasLogged = true;
|
||||
}
|
||||
|
||||
this.lastCullX = camera.scrollX;
|
||||
this.lastCullY = camera.scrollY;
|
||||
|
||||
// ... (rest of setup)
|
||||
|
||||
const view = camera.worldView;
|
||||
const buffer = 200;
|
||||
// Optimization: Adjust buffer based on View Distance setting
|
||||
let buffer = 200;
|
||||
if (this.scene.settings && this.scene.settings.viewDistance === 'LOW') {
|
||||
buffer = 50;
|
||||
}
|
||||
const left = view.x - buffer - this.offsetX;
|
||||
const top = view.y - buffer - this.offsetY;
|
||||
const right = view.x + view.width + buffer - this.offsetX;
|
||||
const bottom = view.y + view.height + buffer - this.offsetY;
|
||||
|
||||
// Calculate visible bounding box (rough)
|
||||
const p1 = this.iso.toGrid(left, top);
|
||||
const p2 = this.iso.toGrid(right, top);
|
||||
const p3 = this.iso.toGrid(left, bottom);
|
||||
@@ -576,6 +604,12 @@ class TerrainSystem {
|
||||
const minGridY = Math.floor(Math.min(p1.y, p2.y, p3.y, p4.y));
|
||||
const maxGridY = Math.ceil(Math.max(p1.y, p2.y, p3.y, p4.y));
|
||||
|
||||
// Debug bounds once
|
||||
if (!this.hasLoggedBounds) {
|
||||
console.log('Culling Bounds:', minGridX, maxGridX, minGridY, maxGridY);
|
||||
this.hasLoggedBounds = true;
|
||||
}
|
||||
|
||||
const startX = Math.max(0, minGridX);
|
||||
const endX = Math.min(this.width, maxGridX);
|
||||
const startY = Math.max(0, minGridY);
|
||||
@@ -587,118 +621,112 @@ class TerrainSystem {
|
||||
|
||||
for (let y = startY; y < endY; y++) {
|
||||
for (let x = startX; x < endX; x++) {
|
||||
const key = `${x},${y}`;
|
||||
neededKeys.add(key);
|
||||
if (x >= 0 && x < this.width && y >= 0 && y < this.height) {
|
||||
const key = `${x},${y}`;
|
||||
neededKeys.add(key);
|
||||
|
||||
// Tile Logic
|
||||
if (!this.visibleTiles.has(key)) {
|
||||
const tilePos = this.iso.toScreen(x, y);
|
||||
const tileData = this.tiles[y][x];
|
||||
// Tile Logic
|
||||
if (!this.visibleTiles.has(key)) {
|
||||
const tilePos = this.iso.toScreen(x, y);
|
||||
const tileData = this.tiles[y][x];
|
||||
|
||||
const sprite = this.tilePool.get();
|
||||
sprite.setTexture(tileData.texture);
|
||||
// Get from Pool
|
||||
const sprite = this.tilePool.get();
|
||||
sprite.setTexture(tileData.texture);
|
||||
|
||||
// Elevation effect: MOČAN vertikalni offset za hribe
|
||||
const elevationOffset = tileData.elevation * -25; // Povečano iz -10 na -25
|
||||
sprite.setPosition(
|
||||
tilePos.x + this.offsetX,
|
||||
tilePos.y + this.offsetY + elevationOffset
|
||||
);
|
||||
// Elevation effect
|
||||
const elevationOffset = tileData.elevation * -25;
|
||||
sprite.setPosition(
|
||||
tilePos.x + this.offsetX,
|
||||
tilePos.y + this.offsetY + elevationOffset
|
||||
);
|
||||
|
||||
// DRAMATIČNO senčenje glede na višino
|
||||
if (tileData.type === 'grass') {
|
||||
let brightness = 1.0;
|
||||
|
||||
if (tileData.elevation > 0.5) {
|
||||
// Visoko = svetlo (1.0 - 1.5)
|
||||
brightness = 1.0 + (tileData.elevation - 0.5) * 1.0;
|
||||
// Senčenje
|
||||
if (tileData.type.includes('grass')) {
|
||||
let brightness = 1.0;
|
||||
if (tileData.elevation > 0.5) {
|
||||
brightness = 1.0 + (tileData.elevation - 0.5) * 1.0;
|
||||
} else {
|
||||
brightness = 0.7 + tileData.elevation * 0.6;
|
||||
}
|
||||
sprite.setTint(Phaser.Display.Color.GetColor(
|
||||
Math.min(255, Math.floor(92 * brightness)),
|
||||
Math.min(255, Math.floor(184 * brightness)),
|
||||
Math.min(255, Math.floor(92 * brightness))
|
||||
));
|
||||
} else {
|
||||
// Nizko = temno (0.7 - 1.0)
|
||||
brightness = 0.7 + tileData.elevation * 0.6;
|
||||
sprite.clearTint();
|
||||
}
|
||||
|
||||
sprite.setTint(Phaser.Display.Color.GetColor(
|
||||
Math.min(255, Math.floor(92 * brightness)),
|
||||
Math.min(255, Math.floor(184 * brightness)),
|
||||
Math.min(255, Math.floor(92 * brightness))
|
||||
));
|
||||
// FIXED DEPTH FOR DEBUG
|
||||
sprite.setDepth(-1000);
|
||||
sprite.setVisible(true);
|
||||
|
||||
this.visibleTiles.set(key, sprite);
|
||||
}
|
||||
|
||||
sprite.setDepth(this.iso.getDepth(x, y) - 2000); // Tiles always in background
|
||||
// Crop Logic
|
||||
if (this.tiles[y][x].hasCrop) {
|
||||
neededCropKeys.add(key);
|
||||
if (!this.visibleCrops.has(key)) {
|
||||
const cropData = this.cropsMap.get(key);
|
||||
if (cropData) {
|
||||
const cropPos = this.iso.toScreen(x, y);
|
||||
const tileData = this.tiles[y][x];
|
||||
const elevationOffset = tileData.elevation * -25;
|
||||
|
||||
this.visibleTiles.set(key, sprite);
|
||||
}
|
||||
const sprite = this.cropPool.get();
|
||||
sprite.setTexture(`crop_stage_${cropData.stage}`);
|
||||
sprite.setPosition(
|
||||
cropPos.x + this.offsetX,
|
||||
cropPos.y + this.offsetY + this.iso.tileHeight / 2 + elevationOffset
|
||||
);
|
||||
// Crop depth = Y pos
|
||||
const depth = this.iso.getDepth(x, y);
|
||||
sprite.setDepth(depth);
|
||||
|
||||
// Elevation effect matching tile logic
|
||||
const tileData = this.tiles[y][x];
|
||||
const elevationOffset = tileData.elevation * -25;
|
||||
|
||||
// Crop Logic
|
||||
if (this.tiles[y][x].hasCrop) {
|
||||
neededCropKeys.add(key);
|
||||
if (!this.visibleCrops.has(key)) {
|
||||
const cropData = this.cropsMap.get(key);
|
||||
if (cropData) {
|
||||
const cropPos = this.iso.toScreen(x, y);
|
||||
const sprite = this.cropPool.get();
|
||||
sprite.setTexture(`crop_stage_${cropData.stage}`);
|
||||
sprite.setPosition(
|
||||
cropPos.x + this.offsetX,
|
||||
cropPos.y + this.offsetY + this.iso.tileHeight / 2 + elevationOffset
|
||||
);
|
||||
// Crop depth = Y pos
|
||||
const depth = this.iso.getDepth(x, y);
|
||||
sprite.setDepth(depth);
|
||||
|
||||
this.visibleCrops.set(key, sprite);
|
||||
this.visibleCrops.set(key, sprite);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Decoration Logic
|
||||
if (this.tiles[y][x].hasDecoration) {
|
||||
neededDecorKeys.add(key);
|
||||
// Decoration Logic
|
||||
if (this.tiles[y][x].hasDecoration) {
|
||||
neededDecorKeys.add(key);
|
||||
|
||||
if (!this.visibleDecorations.has(key)) {
|
||||
// Fast lookup from map
|
||||
const decor = this.decorationsMap.get(key);
|
||||
if (!this.visibleDecorations.has(key)) {
|
||||
// Fast lookup from map
|
||||
const decor = this.decorationsMap.get(key);
|
||||
const tileData = this.tiles[y][x];
|
||||
const elevationOffset = tileData.elevation * -25;
|
||||
|
||||
if (decor) {
|
||||
const decorPos = this.iso.toScreen(x, y);
|
||||
const sprite = this.decorationPool.get();
|
||||
sprite.setTexture(decor.type);
|
||||
if (decor) {
|
||||
const decorPos = this.iso.toScreen(x, y);
|
||||
const sprite = this.decorationPool.get();
|
||||
sprite.setTexture(decor.type);
|
||||
|
||||
// Apply same elevation offset as tile
|
||||
sprite.setPosition(
|
||||
decorPos.x + this.offsetX,
|
||||
decorPos.y + this.offsetY + this.iso.tileHeight / 2 + elevationOffset
|
||||
);
|
||||
// Apply same elevation offset as tile
|
||||
sprite.setPosition(
|
||||
decorPos.x + this.offsetX,
|
||||
decorPos.y + this.offsetY + this.iso.tileHeight / 2 + elevationOffset
|
||||
);
|
||||
|
||||
const depth = this.iso.getDepth(x, y);
|
||||
// Depth strategy: Base of object sorting.
|
||||
// Add small offset based on type if needed, but mainly use Y
|
||||
sprite.setDepth(depth);
|
||||
const depth = this.iso.getDepth(x, y);
|
||||
// Depth strategy: Base of object sorting.
|
||||
// Add small offset based on type if needed, but mainly use Y
|
||||
sprite.setDepth(depth);
|
||||
|
||||
// Apply scale if present
|
||||
if (decor.scale) sprite.setScale(decor.scale);
|
||||
else sprite.setScale(1);
|
||||
// Apply scale if present
|
||||
if (decor.scale) sprite.setScale(decor.scale);
|
||||
else sprite.setScale(1);
|
||||
|
||||
sprite.flipX = (x + y) % 2 === 0;
|
||||
sprite.flipX = (x + y) % 2 === 0;
|
||||
|
||||
// INTERACTIVITY FIX: Allow clicking sprites directly
|
||||
sprite.setInteractive({ pixelPerfect: true, useHandCursor: true });
|
||||
// Sprites are just visual now, interaction handled by InteractionSystem via grid
|
||||
// sprite.setInteractive(...) removed to fix conflicts
|
||||
|
||||
// Clear old listeners
|
||||
sprite.off('pointerdown');
|
||||
// Add click listener
|
||||
sprite.on('pointerdown', (pointer) => {
|
||||
if (this.scene.interactionSystem) {
|
||||
// Manually trigger interaction logic
|
||||
this.scene.interactionSystem.handleDecorationClick(x, y);
|
||||
}
|
||||
});
|
||||
|
||||
this.visibleDecorations.set(key, sprite);
|
||||
this.visibleDecorations.set(key, sprite);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,8 @@ class WeatherSystem {
|
||||
this.currentWeather = 'clear';
|
||||
this.weatherDuration = 0;
|
||||
this.maxWeatherDuration = 10000; // Random duration logic handles this
|
||||
this.rainParticles = []; // {x, y, speed, length}
|
||||
this.rainEmitter = null; // Replaced manual Array with Emitter
|
||||
|
||||
|
||||
// --- State ---
|
||||
this.currentPhase = 'day';
|
||||
@@ -90,20 +91,7 @@ class WeatherSystem {
|
||||
}
|
||||
|
||||
updateWeatherPhysics(delta) {
|
||||
if (this.currentWeather === 'rain' || this.currentWeather === 'storm') {
|
||||
const width = this.scene.scale.width;
|
||||
const height = this.scene.scale.height;
|
||||
|
||||
for (const drop of this.rainParticles) {
|
||||
drop.y += (drop.speed * delta) / 1000;
|
||||
|
||||
// Wrap around
|
||||
if (drop.y > height) {
|
||||
drop.y = -10;
|
||||
drop.x = Math.random() * width;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Optimisation: Physics now handled by Phaser Particles
|
||||
}
|
||||
|
||||
render() {
|
||||
@@ -168,14 +156,7 @@ class WeatherSystem {
|
||||
overlay.fillStyle(0x000033, isDark);
|
||||
overlay.fillRect(0, 0, width, height);
|
||||
|
||||
// Draw drops
|
||||
overlay.lineStyle(1, 0x88aaff, 0.5);
|
||||
for (const drop of this.rainParticles) {
|
||||
overlay.beginPath();
|
||||
overlay.moveTo(drop.x, drop.y);
|
||||
overlay.lineTo(drop.x - 2, drop.y + drop.length);
|
||||
overlay.strokePath();
|
||||
}
|
||||
// Rain drops are now particles (separate GameObject)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -204,22 +185,61 @@ class WeatherSystem {
|
||||
}
|
||||
|
||||
startRain(heavy) {
|
||||
const width = this.scene.scale.width;
|
||||
const height = this.scene.scale.height;
|
||||
const dropCount = heavy ? 150 : 100;
|
||||
const uiScene = this.scene.scene.get('UIScene');
|
||||
if (!uiScene) return;
|
||||
|
||||
for (let i = 0; i < dropCount; i++) {
|
||||
this.rainParticles.push({
|
||||
x: Math.random() * width,
|
||||
y: Math.random() * height,
|
||||
speed: heavy ? Phaser.Math.Between(400, 600) : Phaser.Math.Between(200, 400),
|
||||
length: heavy ? Phaser.Math.Between(10, 15) : Phaser.Math.Between(5, 10)
|
||||
});
|
||||
// Check Settings
|
||||
const quality = this.scene.settings ? this.scene.settings.particles : 'HIGH';
|
||||
if (quality === 'NONE') return;
|
||||
|
||||
// Ensure texture exists
|
||||
if (!this.scene.textures.exists('rain_drop')) {
|
||||
const graphics = this.scene.make.graphics({ x: 0, y: 0, add: false });
|
||||
graphics.fillStyle(0x88aaff, 1);
|
||||
graphics.fillRect(0, 0, 2, 8);
|
||||
graphics.generateTexture('rain_drop', 2, 8);
|
||||
graphics.destroy();
|
||||
}
|
||||
|
||||
// Clean up old
|
||||
if (this.rainEmitter) {
|
||||
this.rainEmitter.destroy();
|
||||
}
|
||||
|
||||
const width = this.scene.scale.width;
|
||||
|
||||
let pQuantity = heavy ? 5 : 2;
|
||||
let pFreq = heavy ? 10 : 30;
|
||||
|
||||
if (quality === 'LOW') {
|
||||
pQuantity = heavy ? 2 : 1;
|
||||
pFreq = heavy ? 50 : 100;
|
||||
}
|
||||
|
||||
// Use modern particles or fallback
|
||||
// Helper to support both if needed, but standard 3.60+ is:
|
||||
this.rainEmitter = uiScene.add.particles(0, 0, 'rain_drop', {
|
||||
x: { min: 0, max: width },
|
||||
y: -20,
|
||||
quantity: pQuantity,
|
||||
frequency: pFreq, // Emit every X ms
|
||||
lifespan: 1500,
|
||||
speedY: { min: heavy ? 600 : 400, max: heavy ? 900 : 600 },
|
||||
speedX: { min: -50, max: 0 }, // Slight wind
|
||||
scaleY: { min: 1.0, max: 2.0 },
|
||||
alpha: { start: 0.6, end: 0 },
|
||||
emitting: true
|
||||
});
|
||||
|
||||
// Depth just above overlay (-1000)
|
||||
this.rainEmitter.setDepth(-990);
|
||||
}
|
||||
|
||||
clearWeather() {
|
||||
this.rainParticles = [];
|
||||
if (this.rainEmitter) {
|
||||
this.rainEmitter.destroy();
|
||||
this.rainEmitter = null;
|
||||
}
|
||||
}
|
||||
|
||||
// --- Getters for Other Systems ---
|
||||
|
||||
Reference in New Issue
Block a user