569 lines
22 KiB
JavaScript
569 lines
22 KiB
JavaScript
// Terrain Generator System
|
|
// Generira proceduralni isometrični teren in skrbi za optimizacijo (Culling, Object Pooling)
|
|
class TerrainSystem {
|
|
constructor(scene, width = 100, height = 100) {
|
|
this.scene = scene;
|
|
this.width = width;
|
|
this.height = height;
|
|
|
|
this.iso = new IsometricUtils(48, 24);
|
|
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.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...');
|
|
|
|
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
|
|
|
|
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;
|
|
|
|
// 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;
|
|
}
|
|
|
|
// 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.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 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);
|
|
|
|
this.tiles[y][x] = {
|
|
gridX: x,
|
|
gridY: y,
|
|
type: terrainType.name,
|
|
texture: terrainType.texture,
|
|
height: noiseValue,
|
|
elevation: elevation,
|
|
yLayer: terrainType.yLayer,
|
|
hasDecoration: false,
|
|
hasCrop: false
|
|
};
|
|
|
|
if (x > 5 && x < this.width - 5 && y > 5 && y < this.height - 5) {
|
|
let decorType = null;
|
|
let maxHp = 1;
|
|
let scale = 1.0;
|
|
|
|
if (terrainType.name.includes('grass')) {
|
|
const rand = Math.random();
|
|
if (elevation > 0.6 && rand < 0.05) {
|
|
decorType = 'bush';
|
|
maxHp = 5;
|
|
} else if (rand < 0.025) {
|
|
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) {
|
|
decorType = 'gravestone';
|
|
maxHp = 10;
|
|
} else if (rand < 0.08) {
|
|
decorType = 'flower';
|
|
maxHp = 1;
|
|
}
|
|
} else if (terrainType.name === 'dirt' && Math.random() < 0.02) {
|
|
decorType = 'bush';
|
|
maxHp = 3;
|
|
}
|
|
|
|
if (decorType) {
|
|
const key = `${x},${y}`;
|
|
const decorData = {
|
|
gridX: x,
|
|
gridY: y,
|
|
type: decorType,
|
|
id: key,
|
|
maxHp: maxHp,
|
|
hp: maxHp,
|
|
scale: scale
|
|
};
|
|
this.decorations.push(decorData);
|
|
this.decorationsMap.set(key, decorData);
|
|
this.tiles[y][x].hasDecoration = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
console.log('✅ Terrain and decorations generated!');
|
|
}
|
|
|
|
damageDecoration(x, y, amount) {
|
|
const key = `${x},${y}`;
|
|
const decor = this.decorationsMap.get(key);
|
|
if (!decor) return false;
|
|
decor.hp -= amount;
|
|
|
|
if (this.visibleDecorations.has(key)) {
|
|
const sprite = this.visibleDecorations.get(key);
|
|
sprite.setTint(0xff0000);
|
|
this.scene.time.delayedCall(100, () => sprite.clearTint());
|
|
this.scene.tweens.add({
|
|
targets: sprite,
|
|
x: sprite.x + 2,
|
|
duration: 50,
|
|
yoyo: true,
|
|
repeat: 1
|
|
});
|
|
}
|
|
|
|
if (decor.hp <= 0) {
|
|
this.removeDecoration(x, y);
|
|
return 'destroyed';
|
|
}
|
|
return 'hit';
|
|
}
|
|
|
|
removeDecoration(x, y) {
|
|
const key = `${x},${y}`;
|
|
const decor = this.decorationsMap.get(key);
|
|
if (!decor) return;
|
|
|
|
if (this.visibleDecorations.has(key)) {
|
|
const sprite = this.visibleDecorations.get(key);
|
|
sprite.setVisible(false);
|
|
this.decorationPool.release(sprite);
|
|
this.visibleDecorations.delete(key);
|
|
}
|
|
|
|
this.decorationsMap.delete(key);
|
|
const index = this.decorations.indexOf(decor);
|
|
if (index > -1) this.decorations.splice(index, 1);
|
|
if (this.tiles[y] && this.tiles[y][x]) this.tiles[y][x].hasDecoration = false;
|
|
|
|
return decor.type;
|
|
}
|
|
|
|
placeStructure(x, y, structureType) {
|
|
if (this.decorationsMap.has(`${x},${y}`)) return false;
|
|
const decorData = {
|
|
gridX: x,
|
|
gridY: y,
|
|
type: structureType,
|
|
id: `${x},${y}`,
|
|
maxHp: 5,
|
|
hp: 5
|
|
};
|
|
this.decorations.push(decorData);
|
|
this.decorationsMap.set(decorData.id, decorData);
|
|
const tile = this.getTile(x, y);
|
|
if (tile) tile.hasDecoration = true;
|
|
this.lastCullX = -9999;
|
|
return true;
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
addCrop(x, y, cropData) {
|
|
const key = `${x},${y}`;
|
|
this.cropsMap.set(key, cropData);
|
|
this.tiles[y][x].hasCrop = true;
|
|
this.lastCullX = -9999;
|
|
}
|
|
|
|
removeCrop(x, y) {
|
|
const key = `${x},${y}`;
|
|
if (this.cropsMap.has(key)) {
|
|
if (this.visibleCrops.has(key)) {
|
|
const sprite = this.visibleCrops.get(key);
|
|
sprite.setVisible(false);
|
|
this.cropPool.release(sprite);
|
|
this.visibleCrops.delete(key);
|
|
}
|
|
this.cropsMap.delete(key);
|
|
this.tiles[y][x].hasCrop = false;
|
|
}
|
|
}
|
|
|
|
updateCropVisual(x, y, stage) {
|
|
const key = `${x},${y}`;
|
|
if (this.visibleCrops.has(key)) {
|
|
const sprite = this.visibleCrops.get(key);
|
|
sprite.setTexture(`crop_stage_${stage}`);
|
|
}
|
|
}
|
|
|
|
init(offsetX, offsetY) {
|
|
this.offsetX = offsetX;
|
|
this.offsetY = offsetY;
|
|
}
|
|
|
|
updateCulling(camera) {
|
|
// Simple Culling
|
|
const view = camera.worldView;
|
|
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;
|
|
|
|
const p1 = this.iso.toGrid(left, top);
|
|
const p2 = this.iso.toGrid(right, top);
|
|
const p3 = this.iso.toGrid(left, bottom);
|
|
const p4 = this.iso.toGrid(right, bottom);
|
|
|
|
const minGridX = Math.floor(Math.min(p1.x, p2.x, p3.x, p4.x));
|
|
const maxGridX = Math.ceil(Math.max(p1.x, p2.x, p3.x, p4.x));
|
|
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));
|
|
|
|
const startX = Math.max(0, minGridX);
|
|
const endX = Math.min(this.width, maxGridX);
|
|
const startY = Math.max(0, minGridY);
|
|
const endY = Math.min(this.height, maxGridY);
|
|
|
|
const neededKeys = 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);
|
|
|
|
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);
|
|
}
|
|
|
|
// DECORATIONS
|
|
const decor = this.decorationsMap.get(key);
|
|
if (decor) {
|
|
neededDecorKeys.add(key);
|
|
if (!this.visibleDecorations.has(key)) {
|
|
const sprite = this.decorationPool.get();
|
|
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);
|
|
} else {
|
|
sprite.setTexture(decor.type);
|
|
sprite.setScale(decor.scale || 1);
|
|
}
|
|
|
|
// 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.setDepth(this.iso.getDepth(x, y) + 1); // Above tile
|
|
this.visibleDecorations.set(key, sprite);
|
|
}
|
|
}
|
|
|
|
// CROPS
|
|
const crop = this.cropsMap.get(key);
|
|
if (crop) {
|
|
neededCropKeys.add(key);
|
|
if (!this.visibleCrops.has(key)) {
|
|
const sprite = this.cropPool.get();
|
|
const screenPos = this.iso.toScreen(x, y);
|
|
sprite.setPosition(screenPos.x + this.offsetX, screenPos.y + this.offsetY);
|
|
sprite.setTexture(`crop_stage_${crop.stage}`);
|
|
sprite.setDepth(this.iso.getDepth(x, y) + 0.5);
|
|
this.visibleCrops.set(key, sprite);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Cleanup tiles
|
|
for (const [key, sprite] of this.visibleTiles) {
|
|
if (!neededKeys.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);
|
|
this.decorationPool.release(sprite);
|
|
this.visibleDecorations.delete(key);
|
|
}
|
|
}
|
|
|
|
// Cleanup crops
|
|
for (const [key, sprite] of this.visibleCrops) {
|
|
if (!neededCropKeys.has(key)) {
|
|
sprite.setVisible(false);
|
|
this.cropPool.release(sprite);
|
|
this.visibleCrops.delete(key);
|
|
}
|
|
}
|
|
}
|
|
}
|