TREE VARIETIES IMPLEMENTED: - Added 5 tree types: Cherry, Oak, Pine, Dead, Apple - AI-generated PNG sprites with transparent backgrounds - Random tree selection for natural diversity - Growth scaling (0.4-0.6x for variety) VISUAL IMPROVEMENTS: - 2.5D depth sorting (Y-position based) - Proper shadows matching tree size - Layered rendering for perspective TRANSPARENCY FIXES: - Disabled automatic sprite processing - User manually removed green screen backgrounds - Fixed pink/red color preservation - Trees now render with proper transparency TREE DISTRIBUTION: - Reduced from ~78 to 10 trees (87% reduction) - Removed corner forest clusters (too crowded) - Sparse scattered placement across 100x100 map - Wider spacing (8-15 tiles apart) - Small tree sizes (0.35-0.7x scale, ~50% reduction) TECHNICAL CHANGES: - PreloadScene: Added tree sprite loading - PreloadScene: Disabled processAllTransparency() - Flat2DTerrainSystem: PNG sprite rendering with fallback - map2d_data.js: Sparse tree generation - Green removal threshold: g > 180 (preserves pink/red) FILES MODIFIED: - src/scenes/PreloadScene.js - src/systems/Flat2DTerrainSystem.js - data/map2d_data.js - assets/sprites/tree_*.png (5 new sprites) - docs/DNEVNIK.md (updated with user availability) Session: 1.5h (23:28-00:48) Date: 14-15.12.2024
224 lines
7.7 KiB
JavaScript
224 lines
7.7 KiB
JavaScript
// 2D Flat Map Data - Generated to match reference images
|
|
// Map size: 100x100 tiles (48x48px each = 4800x4800px world)
|
|
// Style: Stardew Valley smooth 2D top-down
|
|
|
|
const Map2DData = {
|
|
width: 100,
|
|
height: 100,
|
|
tileSize: 48,
|
|
|
|
// Tile type IDs
|
|
tileTypes: {
|
|
GRASS: 0,
|
|
GRASS_FLOWERS: 1,
|
|
DIRT: 2,
|
|
DIRT_EDGE: 3,
|
|
WATER: 4,
|
|
WATER_EDGE: 5,
|
|
STONE: 6,
|
|
TREE: 7,
|
|
FLOWER_RED: 8,
|
|
FLOWER_YELLOW: 9,
|
|
FLOWER_BLUE: 10,
|
|
LILY_PAD: 11,
|
|
BUSH: 12
|
|
},
|
|
|
|
// Map layout - BEAUTIFUL NATURAL DESIGN! 🌳💧🛤️
|
|
generateMap: function () {
|
|
const map = [];
|
|
|
|
// Initialize with grass (some with flowers - 5%)
|
|
for (let y = 0; y < this.height; y++) {
|
|
map[y] = [];
|
|
for (let x = 0; x < this.width; x++) {
|
|
// Mix of clean grass and flowery grass
|
|
map[y][x] = {
|
|
base: Math.random() < 0.05 ? this.tileTypes.GRASS_FLOWERS : this.tileTypes.GRASS,
|
|
decoration: null,
|
|
walkable: true
|
|
};
|
|
}
|
|
}
|
|
|
|
// 1. BEAUTIFUL ORGANIC POND (like reference image!) 💧
|
|
this.addPond(map, 60, 45, 18, 14); // Larger, more organic
|
|
|
|
// 2. NO PATHS - clean grass! 🌿
|
|
// this.addWindingPath(map, 10, 10, 90, 90); // Disabled
|
|
// this.addWindingPath(map, 90, 10, 10, 90); // Disabled
|
|
|
|
// 3. SPARSE SCATTERED TREES 🌳 (NO CLUSTERS!)
|
|
// Removed all corner forests - too crowded!
|
|
|
|
// Just a few scattered trees across entire map
|
|
for (let i = 0; i < 10; i++) { // VERY FEW! (was 15)
|
|
this.addTreeCluster(map,
|
|
10 + Math.random() * 80, // Entire map
|
|
10 + Math.random() * 80,
|
|
1 // Single trees only
|
|
);
|
|
}
|
|
|
|
|
|
// 4. COLORFUL FLOWERS SCATTERED 🌸
|
|
this.addFlowers(map, 30);
|
|
|
|
// 5. BUSHES near tree clusters 🌿
|
|
this.addBushes(map, 15);
|
|
|
|
// 6. NO PUDDLES (no paths anymore!)
|
|
// this.addPuddlesAlongPaths(map, 8); // Disabled
|
|
|
|
return map;
|
|
},
|
|
|
|
addPond: function (map, centerX, centerY, width, height) {
|
|
// PERFECTLY ROUND/CIRCULAR pond! 🔵
|
|
const radius = Math.min(width, height) / 2;
|
|
|
|
for (let y = -radius; y < radius; y++) {
|
|
for (let x = -radius; x < radius; x++) {
|
|
const dist = Math.sqrt(x * x + y * y);
|
|
|
|
// Perfect circle - no noise!
|
|
if (dist < radius) {
|
|
const tileX = Math.floor(centerX + x);
|
|
const tileY = Math.floor(centerY + y);
|
|
|
|
if (tileX >= 0 && tileX < this.width && tileY >= 0 && tileY < this.height) {
|
|
// Edge or center (smooth transition)
|
|
if (dist > radius - 2) {
|
|
map[tileY][tileX].base = this.tileTypes.WATER_EDGE;
|
|
} else {
|
|
map[tileY][tileX].base = this.tileTypes.WATER;
|
|
}
|
|
map[tileY][tileX].walkable = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Add lily pads in circular pattern
|
|
for (let i = 0; i < 4; i++) {
|
|
const angle = (Math.PI * 2 * i) / 4 + Math.random() * 0.5;
|
|
const r = radius * (0.4 + Math.random() * 0.3);
|
|
const lx = Math.floor(centerX + Math.cos(angle) * r);
|
|
const ly = Math.floor(centerY + Math.sin(angle) * r);
|
|
|
|
if (lx >= 0 && lx < this.width && ly >= 0 && ly < this.height) {
|
|
if (map[ly][lx].base === this.tileTypes.WATER) {
|
|
map[ly][lx].decoration = this.tileTypes.LILY_PAD;
|
|
}
|
|
}
|
|
}
|
|
},
|
|
|
|
addWindingPath: function (map, startX, startY, endX, endY) {
|
|
const steps = 50;
|
|
const pathWidth = 1; // NARROW path - 1 tile wide!
|
|
|
|
for (let i = 0; i <= steps; i++) {
|
|
const t = i / steps;
|
|
|
|
// Cubic curve for natural winding
|
|
const x = startX + (endX - startX) * t + Math.sin(t * Math.PI * 3) * 8;
|
|
const y = startY + (endY - startY) * t + Math.cos(t * Math.PI * 2) * 6;
|
|
|
|
// Draw path with width
|
|
for (let py = -pathWidth; py <= pathWidth; py++) {
|
|
for (let px = -pathWidth; px <= pathWidth; px++) {
|
|
const dist = Math.sqrt(px * px + py * py);
|
|
if (dist <= pathWidth) {
|
|
const tileX = Math.floor(x + px);
|
|
const tileY = Math.floor(y + py);
|
|
|
|
if (tileX >= 0 && tileX < this.width && tileY >= 0 && tileY < this.height) {
|
|
if (map[tileY][tileX].base !== this.tileTypes.WATER) {
|
|
if (dist > pathWidth - 0.5) {
|
|
map[tileY][tileX].base = this.tileTypes.DIRT_EDGE;
|
|
} else {
|
|
map[tileY][tileX].base = this.tileTypes.DIRT;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
|
|
addPuddlesAlongPaths: function (map, count) {
|
|
let placed = 0;
|
|
let attempts = 0;
|
|
|
|
while (placed < count && attempts < count * 10) {
|
|
const x = Math.floor(Math.random() * this.width);
|
|
const y = Math.floor(Math.random() * this.height);
|
|
|
|
// Check if near path edge
|
|
if (map[y][x].base === this.tileTypes.DIRT_EDGE ||
|
|
map[y][x].base === this.tileTypes.DIRT) {
|
|
// Small puddle (already have sprite!)
|
|
map[y][x].decoration = 'puddle';
|
|
placed++;
|
|
}
|
|
attempts++;
|
|
}
|
|
},
|
|
|
|
addTreeCluster: function (map, centerX, centerY, count) {
|
|
for (let i = 0; i < count; i++) {
|
|
const angle = (Math.PI * 2 * i) / count + Math.random() * 0.5;
|
|
const radius = 8 + Math.random() * 7; // EVEN WIDER! (was 5+7)
|
|
const tx = Math.floor(centerX + Math.cos(angle) * radius);
|
|
const ty = Math.floor(centerY + Math.sin(angle) * radius);
|
|
|
|
if (tx >= 0 && tx < this.width && ty >= 0 && ty < this.height) {
|
|
if (map[ty][tx].walkable && map[ty][tx].base === this.tileTypes.GRASS) {
|
|
map[ty][tx].decoration = this.tileTypes.TREE;
|
|
map[ty][tx].walkable = false;
|
|
}
|
|
}
|
|
}
|
|
},
|
|
|
|
addFlowers: function (map, count) {
|
|
const flowerTypes = [
|
|
this.tileTypes.FLOWER_RED,
|
|
this.tileTypes.FLOWER_YELLOW,
|
|
this.tileTypes.FLOWER_BLUE
|
|
];
|
|
|
|
for (let i = 0; i < count; i++) {
|
|
const x = Math.floor(Math.random() * this.width);
|
|
const y = Math.floor(Math.random() * this.height);
|
|
|
|
if (map[y][x].base === this.tileTypes.GRASS &&
|
|
!map[y][x].decoration &&
|
|
map[y][x].walkable) {
|
|
map[y][x].decoration = flowerTypes[Math.floor(Math.random() * flowerTypes.length)];
|
|
}
|
|
}
|
|
},
|
|
|
|
addBushes: function (map, count) {
|
|
for (let i = 0; i < count; i++) {
|
|
const x = Math.floor(Math.random() * this.width);
|
|
const y = Math.floor(Math.random() * this.height);
|
|
|
|
if (map[y][x].base === this.tileTypes.GRASS &&
|
|
!map[y][x].decoration &&
|
|
map[y][x].walkable) {
|
|
map[y][x].decoration = this.tileTypes.BUSH;
|
|
map[y][x].walkable = false;
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
// Export for use
|
|
if (typeof module !== 'undefined' && module.exports) {
|
|
module.exports = Map2DData;
|
|
}
|