Files
novafarma/data/map2d_data.js
NovaFarma Dev 80bddf5d61 feat: Complete 2D Visual Overhaul - Isometric to Flat Top-Down
- NEW: Flat2DTerrainSystem.js (375 lines)
- NEW: map2d_data.js procedural map (221 lines)
- MODIFIED: GameScene async create, 2D terrain integration
- MODIFIED: Player.js flat 2D positioning
- MODIFIED: game.js disabled pixelArt for smooth rendering
- FIXED: 15+ bugs (updateCulling, isometric conversions, grid lines)
- ADDED: Phase 28 to TASKS.md
- DOCS: DNEVNIK.md session summary

Result: Working flat 2D game with Stardew Valley style!
Time: 5.5 hours
2025-12-14 17:12:40 +01:00

212 lines
7.3 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 - CLEAN MINIMAL DESIGN!
generateMap: function () {
const map = [];
// Initialize with CLEAN grass (very few flowers)
for (let y = 0; y < this.height; y++) {
map[y] = [];
for (let x = 0; x < this.width; x++) {
// Mostly clean grass
map[y][x] = {
base: Math.random() < 0.03 ? this.tileTypes.GRASS_FLOWERS : this.tileTypes.GRASS,
decoration: null,
walkable: true
};
}
}
// Add ONE pond (center)
this.addPond(map, 50, 50, 12, 10);
// MINIMAL trees - just 4 small clusters
this.addTreeCluster(map, 20, 20, 2);
this.addTreeCluster(map, 80, 20, 2);
this.addTreeCluster(map, 20, 80, 2);
this.addTreeCluster(map, 80, 80, 2);
// Very few flowers
this.addFlowers(map, 10);
// NO paths - keep it clean!
// NO bushes - too busy!
return map;
},
addPond: function (map, centerX, centerY, width, height) {
// Organic pond shape (not perfect rectangle)
for (let y = -height / 2; y < height / 2; y++) {
for (let x = -width / 2; x < width / 2; x++) {
const dx = x / (width / 2);
const dy = y / (height / 2);
const dist = Math.sqrt(dx * dx + dy * dy);
// Create organic edge
const noise = Math.sin(x * 0.5) * 0.2 + Math.cos(y * 0.3) * 0.2;
if (dist < 1.0 + noise) {
const tileX = Math.floor(centerX + x);
const tileY = Math.floor(centerY + y);
if (tileX >= 0 && tileX < this.width && tileY >= 0 && tileY < this.height) {
// Check if edge or center
if (dist > 0.85 + noise) {
map[tileY][tileX].base = this.tileTypes.WATER_EDGE;
} else {
map[tileY][tileX].base = this.tileTypes.WATER;
}
map[tileY][tileX].walkable = false;
}
}
}
}
// Add lily pads (3-5 random positions in pond)
for (let i = 0; i < 4; i++) {
const angle = (Math.PI * 2 * i) / 4 + Math.random() * 0.5;
const radius = (width / 2) * (0.4 + Math.random() * 0.3);
const lx = Math.floor(centerX + Math.cos(angle) * radius);
const ly = Math.floor(centerY + Math.sin(angle) * radius);
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 = 2 + Math.floor(Math.random() * 2); // 2-3 tiles 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 = 2 + Math.random() * 3;
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;
}