Tiled Map Editor Exploration + Bug Fixes

Tiled Setup:
- Installed Tiled v1.11.2 (winget)
- Created workflow documentation (.agent/workflows/tiled-map-setup.md)
- Generated demo Tiled map files (farm_map.tmx, .json, .tsx)
- Created TILED_INTEGRATION_STATUS.md documentation

 Bug Fixes:
- SaveSystem.js: Fixed compatibility with Flat2DTerrainSystem
- InteractionSystem.js: Added null checks for terrainSystem
- PreloadScene.js: Tiled asset loading (currently not used)

 Documentation:
- Created DNEVNIK.md (development diary)
- Updated QUICK_TASK_REFERENCE.md with recent work

 Note: Tiled integration incomplete (tileset size issues)
- Reverted to procedural Flat2DTerrainSystem (working)
- Future work: Create proper 192x192 tileset PNGs

Session: 2h (20:00-22:00)
Date: 14.12.2024
This commit is contained in:
2025-12-14 22:55:29 +01:00
parent 80bddf5d61
commit a4d2d137ec
20 changed files with 1248 additions and 186 deletions

View File

@@ -44,105 +44,114 @@ class Flat2DTerrainSystem {
}
createTileTextures() {
// 🎨 Create SOLID, OPAQUE tiles (no transparency!)
const graphics = this.scene.make.graphics({ x: 0, y: 0, add: false });
const size = this.tileSize;
// GRASS - VIBRANT RICH GREEN! 🌿
// Check if PNG tilesets are loaded
const hasGrass = this.scene.textures.exists('tileset_grass');
const hasWater = this.scene.textures.exists('tileset_water');
const hasDirt = this.scene.textures.exists('tileset_dirt');
if (hasGrass && hasWater && hasDirt) {
console.log('✅ PNG Tilesets found! Creating beautiful tiles...');
// GRASS - SUPER VIBRANT GREEN! 🌿 (MORE 2D!)
graphics.clear();
graphics.fillStyle(0x3CB371, 1.0); // Medium sea green - vibrant!
graphics.fillRect(0, 0, size, size);
// Darker spots for contrast
for (let i = 0; i < 20; i++) {
graphics.fillStyle(0x2E8B57, 0.5);
graphics.fillCircle(Math.random() * size, Math.random() * size, 2 + Math.random() * 4);
}
// Lighter highlights
for (let i = 0; i < 15; i++) {
graphics.fillStyle(0x90EE90, 0.4);
graphics.fillCircle(Math.random() * size, Math.random() * size, 1.5);
}
graphics.generateTexture('tile2d_grass', size, size);
// GRASS WITH FLOWERS (VIBRANT!)
graphics.clear();
graphics.fillStyle(0x3CB371, 1.0);
graphics.fillRect(0, 0, size, size);
for (let i = 0; i < 12; i++) {
graphics.fillStyle(0x2E8B57, 0.3);
graphics.fillCircle(Math.random() * size, Math.random() * size, 2);
}
// BRIGHT colorful flowers!
const flowerColors = [0xFF1493, 0xFFD700, 0x00BFFF, 0xFF69B4];
for (let i = 0; i < 4; i++) {
graphics.fillStyle(flowerColors[Math.floor(Math.random() * 4)], 1.0);
graphics.fillCircle(Math.random() * size, Math.random() * size, 3);
}
graphics.generateTexture('tile2d_grass_flowers', size, size);
// DIRT - RICH BROWN! 🟤
graphics.clear();
graphics.fillStyle(0x8B4513, 1.0); // Saddle brown
graphics.fillRect(0, 0, size, size);
for (let i = 0; i < 25; i++) {
graphics.fillStyle(0x654321, 0.5);
graphics.fillCircle(Math.random() * size, Math.random() * size, 3 + Math.random() * 5);
}
for (let i = 0; i < 15; i++) {
graphics.fillStyle(0xA0826D, 0.4);
graphics.fillCircle(Math.random() * size, Math.random() * size, 2);
}
graphics.generateTexture('tile2d_dirt', size, size);
graphics.clear();
graphics.fillStyle(0x8b4513, 1.0);
graphics.fillRect(0, 0, size, size);
graphics.fillStyle(0x3CB371, 0.3);
graphics.fillRect(0, 0, size, size);
graphics.generateTexture('tile2d_dirt_edge', size, size);
// WATER - BRIGHT TEAL! 💧 (WITH OUTLINE!)
graphics.clear();
graphics.fillStyle(0x20B2AA, 1.0); // Light sea green - vibrant teal!
graphics.fillRect(0, 0, size, size);
// Dark outline border (2D style!)
graphics.lineStyle(2, 0x006994, 0.8);
graphics.strokeRect(1, 1, size - 2, size - 2);
for (let i = 0; i < 10; i++) {
graphics.fillStyle(0x008B8B, 0.4);
graphics.fillCircle(Math.random() * size, Math.random() * size, 5 + Math.random() * 8);
}
for (let i = 0; i < 15; i++) {
graphics.fillStyle(0x48D1CC, 0.5);
graphics.fillCircle(Math.random() * size, Math.random() * size, 2);
}
for (let i = 0; i < 12; i++) {
graphics.fillStyle(0xFFFFFF, 0.6);
graphics.fillCircle(Math.random() * size, Math.random() * size, 1);
}
graphics.generateTexture('tile2d_water', size, size);
graphics.clear();
graphics.fillStyle(0x20B2AA, 1.0);
graphics.fillRect(0, 0, size, size);
graphics.fillStyle(0x48D1CC, 0.5);
graphics.fillRect(0, 0, size, size);
graphics.generateTexture('tile2d_water_edge', size, size);
} else {
console.warn('⚠️ PNG Tilesets not loaded! Using fallback colors');
}
// STONE (FULLY OPAQUE!)
graphics.clear();
graphics.fillStyle(0x59b36a); // BRIGHT rich green!
graphics.fillStyle(0x808080, 1.0); // 100% opacity!
graphics.fillRect(0, 0, size, size);
// Add grass texture - DARKER spots
for (let i = 0; i < 15; i++) {
const x = Math.random() * size;
const y = Math.random() * size;
graphics.fillStyle(0x3a8d4f, 0.5);
graphics.fillCircle(x, y, 2 + Math.random() * 3);
}
// LIGHTER highlights
for (let i = 0; i < 10; i++) {
const x = Math.random() * size;
const y = Math.random() * size;
graphics.fillStyle(0x7ad389, 0.6);
graphics.fillCircle(x, y, 1.5);
}
graphics.generateTexture('tile2d_grass', size, size);
// GRASS WITH FLOWERS
graphics.clear();
graphics.fillStyle(0x4a9d5f);
graphics.fillRect(0, 0, size, size);
// Grass texture
for (let i = 0; i < 10; i++) {
graphics.fillStyle(0x3a8d4f, 0.4);
graphics.fillCircle(Math.random() * size, Math.random() * size, 1.5);
}
// Small flowers
const flowerColors = [0xff6b6b, 0xffd93d, 0x6bcbff];
for (let i = 0; i < 3; i++) {
graphics.fillStyle(flowerColors[Math.floor(Math.random() * 3)]);
graphics.fillCircle(Math.random() * size, Math.random() * size, 2);
}
graphics.generateTexture('tile2d_grass_flowers', size, size);
// DIRT - VIBRANT BROWN! 🟤
graphics.clear();
graphics.fillStyle(0xa87f5a); // BRIGHT brown!
graphics.fillRect(0, 0, size, size);
// Dirt texture - darker clumps
for (let i = 0; i < 20; i++) {
graphics.fillStyle(0x7a5f3a, 0.6);
graphics.fillCircle(Math.random() * size, Math.random() * size, 3 + Math.random() * 4);
}
// Lighter spots
for (let i = 0; i < 12; i++) {
graphics.fillStyle(0xc89f6f, 0.5);
graphics.fillCircle(Math.random() * size, Math.random() * size, 2);
}
graphics.generateTexture('tile2d_dirt', size, size);
// DIRT EDGE - Transition to grass
graphics.clear();
graphics.fillGradientStyle(0x8b6f47, 0x8b6f47, 0x6a9d5f, 0x6a9d5f, 1);
graphics.fillRect(0, 0, size, size);
graphics.generateTexture('tile2d_dirt_edge', size, size);
// WATER - BRIGHT BLUE! 💧
graphics.clear();
graphics.fillStyle(0x3498db); // VIBRANT blue!
graphics.fillRect(0, 0, size, size);
// Water highlights - darker depth
for (let i = 0; i < 8; i++) {
graphics.fillStyle(0x2078ab, 0.4);
graphics.fillCircle(Math.random() * size, Math.random() * size, 4 + Math.random() * 6);
}
// Light reflections
for (let i = 0; i < 12; i++) {
graphics.fillStyle(0x5dade2, 0.5);
graphics.fillCircle(Math.random() * size, Math.random() * size, 2);
}
// White sparkles
for (let i = 0; i < 10; i++) {
graphics.fillStyle(0xffffff, 0.6);
graphics.fillCircle(Math.random() * size, Math.random() * size, 1);
}
graphics.generateTexture('tile2d_water', size, size);
// WATER EDGE - Lighter border
graphics.clear();
graphics.fillGradientStyle(0x4aacdc, 0x4aacdc, 0x1a5f7a, 0x1a5f7a, 0.7);
graphics.fillRect(0, 0, size, size);
graphics.generateTexture('tile2d_water_edge', size, size);
// STONE - Gray
graphics.clear();
graphics.fillStyle(0x808080);
graphics.fillRect(0, 0, size, size);
for (let i = 0; i < 12; i++) {
graphics.fillStyle(0x606060, 0.6);
graphics.fillCircle(Math.random() * size, Math.random() * size, 2 + Math.random() * 3);
@@ -150,54 +159,105 @@ class Flat2DTerrainSystem {
graphics.generateTexture('tile2d_stone', size, size);
graphics.destroy();
this.texturesReady = true;
console.log('✅ Tile textures created');
console.log('✅ Tile textures created - FULLY OPAQUE!');
}
extractTilesFromTileset(sourceKey, targetKey, tileX, tileY, tileSize) {
// Extract a single tile from tileset PNG and create new texture
if (!this.scene.textures.exists(sourceKey)) {
console.warn(`⚠️ Tileset ${sourceKey} not found, using fallback`);
return;
}
const source = this.scene.textures.get(sourceKey).getSourceImage();
const canvas = document.createElement('canvas');
canvas.width = tileSize;
canvas.height = tileSize;
const ctx = canvas.getContext('2d');
// Draw specific tile from tileset
ctx.drawImage(
source,
tileX * tileSize, tileY * tileSize, // Source position
tileSize, tileSize, // Source size
0, 0, // Dest position
tileSize, tileSize // Dest size
);
// Create new texture from extracted tile
this.scene.textures.addCanvas(targetKey, canvas);
}
renderMap() {
// Create layer containers
this.groundLayer = this.scene.add.container(0, 0);
this.pathsLayer = this.scene.add.container(0, 0);
this.decorLayer = this.scene.add.container(0, 0);
// 🎨 SIMPLE & CLEAN: Use TileSprite for seamless background!
const size = this.tileSize;
const mapWidth = this.width * size;
const mapHeight = this.height * size;
// Set depths
this.groundLayer.setDepth(1);
console.log('🎨 Rendering seamless 2D map...');
// Create solid grass background (NO TRANSPARENCY!)
const grassBG = this.scene.add.tileSprite(0, 0, mapWidth, mapHeight, 'tile2d_grass');
grassBG.setOrigin(0, 0);
grassBG.setDepth(1);
// Create containers for paths and decorations
this.pathsLayer = this.scene.add.container(0, 0);
this.pathsLayer.setDepth(2);
this.decorLayer = this.scene.add.container(0, 0);
this.decorLayer.setDepth(3);
const size = this.tileSize;
const graphics = this.scene.make.graphics({ x: 0, y: 0, add: false });
// Render all tiles
// Array to store water sprites for animation
this.waterSprites = [];
// Render all tiles on top of grass background
for (let y = 0; y < this.height; y++) {
for (let x = 0; x < this.width; x++) {
const tile = this.tiles[y][x];
const worldX = x * size;
const worldY = y * size;
// Get texture key from tile type
const textureKey = this.getTileTexture(tile.base);
// Create tile sprite
const tileSprite = this.scene.add.image(worldX, worldY, textureKey);
tileSprite.setOrigin(0, 0);
tileSprite.setDisplaySize(size, size);
// Add to appropriate layer
if (tile.base <= 1) {
this.groundLayer.add(tileSprite);
} else {
this.pathsLayer.add(tileSprite);
// WATER tiles - back to Image, animate with tint!
if (tile.base === 4 || tile.base === 5) {
const waterSprite = this.scene.add.image(worldX, worldY, 'tile2d_water');
waterSprite.setOrigin(0, 0);
waterSprite.setDisplaySize(size, size);
this.pathsLayer.add(waterSprite);
this.waterSprites.push(waterSprite); // Store for animation!
}
// Add decoration if exists
// DIRT PATH tiles
else if (tile.base === 2 || tile.base === 3) {
const dirtSprite = this.scene.add.image(worldX, worldY, 'tile2d_dirt');
dirtSprite.setOrigin(0, 0);
dirtSprite.setDisplaySize(size, size);
// Edge blending
if (tile.base === 3) {
dirtSprite.setAlpha(0.8); // Blend with grass
}
this.pathsLayer.add(dirtSprite);
}
// DECORATIONS
if (tile.decoration) {
this.addDecoration(x, y, tile.decoration);
}
}
}
console.log('✅ Map rendered: 3 layers created');
graphics.destroy();
// Store reference
this.groundLayer = grassBG;
console.log('✅ Seamless map rendered (NO GRID!)');
console.log(`🌊 Water tiles animated: ${this.waterSprites.length}`);
}
getTileTexture(tileType) {
@@ -256,20 +316,50 @@ class Flat2DTerrainSystem {
createTree(x, y) {
const graphics = this.scene.add.graphics();
// Trunk
// DROP SHADOW (2D depth!) 🎨
graphics.fillStyle(0x000000, 0.15);
graphics.fillEllipse(x + 1, y + 24, 20, 6);
// TRUNK - simple rectangle
graphics.fillStyle(0x8B4513);
graphics.fillRect(x - 6, y, 12, 20);
graphics.fillRect(x - 4, y + 6, 8, 18);
// Crown (round)
graphics.fillStyle(0x2d5016, 0.9);
graphics.fillCircle(x, y - 10, 18);
// Trunk outline
graphics.lineStyle(1, 0x654321);
graphics.strokeRect(x - 4, y + 6, 8, 18);
graphics.fillStyle(0x3a6b1f, 0.8);
graphics.fillCircle(x - 5, y - 12, 14);
graphics.fillCircle(x + 5, y - 8, 12);
// CHERRY BLOSSOM CROWN - FLAT 2D TRIANGLE! 🌸
// Base pink triangle
graphics.fillStyle(0xFF69B4);
graphics.fillTriangle(
x, y - 18, // Top point
x - 16, y + 8, // Bottom left
x + 16, y + 8 // Bottom right
);
graphics.fillStyle(0x4a8d2f, 0.7);
graphics.fillCircle(x, y - 15, 10);
// Dark outline (2D cartoon style!)
graphics.lineStyle(2, 0xDB7093, 1.0);
graphics.strokeTriangle(
x, y - 18,
x - 16, y + 8,
x + 16, y + 8
);
// Inner lighter triangle (highlight)
graphics.fillStyle(0xFFB6C1);
graphics.fillTriangle(
x, y - 14,
x - 10, y + 4,
x + 10, y + 4
);
// Top highlight (blossom peak!)
graphics.fillStyle(0xFFFFFF, 0.7);
graphics.fillTriangle(
x, y - 14,
x - 6, y - 4,
x + 6, y - 4
);
return graphics;
}
@@ -332,15 +422,15 @@ class Flat2DTerrainSystem {
// Use existing puddle sprite if available
if (this.scene.textures.exists('luza_sprite')) {
const sprite = this.scene.add.image(x, y, 'luza_sprite');
sprite.setScale(0.8);
sprite.setAlpha(0.4);
sprite.setScale(0.3); // SMALL puddles!
sprite.setAlpha(0.3); // More transparent
return sprite;
}
// Fallback
// Fallback - small ellipse
const graphics = this.scene.add.graphics();
graphics.fillStyle(0x4488bb, 0.5);
graphics.fillEllipse(x, y, 12, 8);
graphics.fillStyle(0x4488bb, 0.4);
graphics.fillEllipse(x, y, 8, 5); // Smaller!
return graphics;
}
@@ -375,7 +465,22 @@ class Flat2DTerrainSystem {
}
update(time, delta) {
// Reserved for animations (water waves, etc)
// 🌊 ANIMATED WATER - shimmer effect with tint!
if (this.waterSprites && this.waterSprites.length > 0) {
this.waterSprites.forEach(sprite => {
if (sprite && sprite.active) {
// Wave shimmer using tint color
const wave = Math.sin(time * 0.002) * 0.15 + 0.85;
const tintValue = Math.floor(wave * 255);
sprite.setTint(
0x2A7FBC | (tintValue << 16) | (tintValue << 8) | tintValue
);
// Subtle alpha pulsing
sprite.setAlpha(0.95 + Math.sin(time * 0.001) * 0.05);
}
});
}
}
destroy() {

View File

@@ -304,9 +304,10 @@ class InteractionSystem {
// 4. Farming is now handled via Player Space key (handleFarmingAction)
// No click farming anymore
// 5. Try damage decoration
const id = `${gridX},${gridY}`;
if (this.scene.terrainSystem.decorationsMap.has(id)) {
if (this.scene.terrainSystem && this.scene.terrainSystem.decorationsMap.has(id)) {
const decor = this.scene.terrainSystem.decorationsMap.get(id);
// Workstation Interaction

View File

@@ -21,15 +21,17 @@ class SaveSystem {
y: npc.gridY
}));
// Zberi podatke o terenu
const terrainSeed = this.scene.terrainSystem.noise.seed;
// Zberi podatke o terenu - FLAT 2D (no seed, no crops yet!)
const terrainSeed = this.scene.terrainSystem.noise?.seed || 0; // Optional for old system
// Zberi dinamične podatke terena (Crops & Modified Decor/Buildings)
const cropsData = Array.from(this.scene.terrainSystem.cropsMap.entries());
// We only save Decorations that are NOT default?
// For simplicity, let's save ALL current decorations, and on Load clear everything and rebuild.
// Actually, decorationsMap contains objects with gridX, gridY, type.
const decorData = Array.from(this.scene.terrainSystem.decorationsMap.values());
// Flat2DTerrainSystem doesn't have cropsMap yet - skip for now
const cropsData = this.scene.terrainSystem.cropsMap
? Array.from(this.scene.terrainSystem.cropsMap.entries())
: [];
const decorData = this.scene.terrainSystem.decorationsMap
? Array.from(this.scene.terrainSystem.decorationsMap.values())
: [];
// Inventory
const inventoryData = {
@@ -38,13 +40,13 @@ class SaveSystem {
};
const saveData = {
version: 2.4, // Nazaj na pixel art
version: 2.5, // 2D Flat Version
timestamp: Date.now(),
player: { x: playerPos.x, y: playerPos.y },
terrain: {
seed: terrainSeed,
crops: cropsData, // array of [key, value]
decorations: decorData // array of objects
crops: cropsData,
decorations: decorData
},
npcs: npcsData,
inventory: inventoryData,