126 lines
4.6 KiB
JavaScript
126 lines
4.6 KiB
JavaScript
import { TILE, SEA_GY, BEDROCK_GY, GEN_MARGIN_X } from '../core/constants.js';
|
||
import { state } from '../core/state.js';
|
||
import { grid, blocks, setBlock, getBlock, k } from './world-storage.js';
|
||
|
||
// Генерация (по X, на всю глубину до bedrock)
|
||
const generated = state.generated; // Set of gx already generated
|
||
|
||
function surfaceGyAt(gx) {
|
||
// базовая поверхность выше уровня воды с вариациями + "горы"
|
||
// Используем seed для детерминированной генерации
|
||
// Увеличили амплитуду и добавили больше частот для разнообразия
|
||
const n1 = Math.sin(gx * 0.025 + state.worldSeed * 0.001) * 8; // крупные горы
|
||
const n2 = Math.sin(gx * 0.012 + state.worldSeed * 0.002) * 12; // средние горы
|
||
const n3 = Math.sin(gx * 0.006 + state.worldSeed * 0.003) * 6; // мелкие холмы
|
||
const n4 = Math.sin(gx * 0.045 + state.worldSeed * 0.004) * 4; // детали
|
||
const n5 = Math.cos(gx * 0.018 + state.worldSeed * 0.005) * 5; // дополнительные вариации
|
||
const h = Math.floor(SEA_GY - 8 + n1 + n2 + n3 + n4 + n5); // чем меньше gy - тем выше
|
||
return h;
|
||
}
|
||
|
||
function genColumn(gx) {
|
||
if (generated.has(gx)) return;
|
||
generated.add(gx);
|
||
|
||
const sgy = surfaceGyAt(gx);
|
||
|
||
// вода (если поверхность ниже уровня моря => sgy > SEA_GY)
|
||
if (sgy > SEA_GY) {
|
||
for (let gy = SEA_GY; gy < sgy; gy++) {
|
||
setBlock(gx, gy, 'water');
|
||
}
|
||
// пляж
|
||
setBlock(gx, sgy, 'sand');
|
||
} else {
|
||
// верхний блок: снег на высоких точках
|
||
if (sgy < SEA_GY - 10) setBlock(gx, sgy, 'stone');
|
||
else setBlock(gx, sgy, 'grass');
|
||
}
|
||
|
||
// подповерхностные слои
|
||
for (let gy = sgy + 1; gy <= BEDROCK_GY; gy++) {
|
||
if (gy === BEDROCK_GY) {
|
||
setBlock(gx, gy, 'bedrock');
|
||
continue;
|
||
}
|
||
|
||
let t = 'stone';
|
||
|
||
// ближе к поверхности
|
||
if (gy <= sgy + 3) t = 'dirt';
|
||
|
||
// биомы/материалы
|
||
if (sgy > SEA_GY && gy === sgy + 1 && seededRandom(gx, gy) < 0.25) t = 'clay';
|
||
if (gy > sgy + 6 && seededRandom(gx, gy) < 0.07) t = 'gravel';
|
||
|
||
// руды: чем глубже, тем интереснее
|
||
const depth = gy - sgy;
|
||
const r = seededRandom(gx, gy);
|
||
if (t === 'stone') {
|
||
if (r < 0.06) t = 'coal';
|
||
else if (r < 0.10) t = 'copper_ore';
|
||
else if (r < 0.13) t = 'iron_ore';
|
||
else if (depth > 40 && r < 0.145) t = 'gold_ore';
|
||
else if (depth > 70 && r < 0.152) t = 'diamond_ore';
|
||
}
|
||
|
||
setBlock(gx, gy, t);
|
||
}
|
||
|
||
// Деревья и цветы (только на траве, и не в воде)
|
||
const top = getBlock(gx, sgy);
|
||
if (top && top.t === 'grass') {
|
||
if (seededRandom(gx, sgy - 1) < 0.10) {
|
||
setBlock(gx, sgy - 1, 'flower');
|
||
}
|
||
if (seededRandom(gx, sgy - 2) < 0.12) {
|
||
// простое дерево
|
||
setBlock(gx, sgy - 1, 'wood');
|
||
setBlock(gx, sgy - 2, 'wood');
|
||
setBlock(gx, sgy - 3, 'leaves');
|
||
setBlock(gx - 1, sgy - 3, 'leaves');
|
||
setBlock(gx + 1, sgy - 3, 'leaves');
|
||
}
|
||
}
|
||
|
||
// Применяем серверные оверрайды для этой колонны
|
||
const colPrefix = gx + ',';
|
||
for (const [key, ov] of state.serverOverrides) {
|
||
if (!key.startsWith(colPrefix)) continue;
|
||
if (ov.op === 'remove') {
|
||
const b = grid.get(key);
|
||
if (b) { grid.delete(key); b.dead = true; }
|
||
} else if (ov.op === 'set') {
|
||
if (!grid.has(key)) {
|
||
const gy = parseInt(key.split(',')[1]);
|
||
const nb = { gx, gy, t: ov.t, dead: false, active: false, fuse: 0 };
|
||
grid.set(key, nb);
|
||
blocks.push(nb);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// Перегенерация видимых чанков (используется при загрузке сохранения)
|
||
function regenerateVisibleChunks() {
|
||
const gx0 = Math.floor(state.camX / TILE);
|
||
for (let gx = gx0 - GEN_MARGIN_X; gx <= gx0 + GEN_MARGIN_X; gx++) {
|
||
// Принудительно перегенерируем колонну
|
||
generated.delete(gx);
|
||
genColumn(gx);
|
||
}
|
||
}
|
||
|
||
function ensureGenAroundCamera() {
|
||
const gx0 = Math.floor(state.camX / TILE);
|
||
for (let gx = gx0 - GEN_MARGIN_X; gx <= gx0 + GEN_MARGIN_X; gx++) {
|
||
genColumn(gx);
|
||
}
|
||
}
|
||
|
||
function seededRandom(gx, gy) {
|
||
const n = Math.sin(gx * 12.9898 + gy * 78.233 + state.worldSeed * 0.1) * 43758.5453;
|
||
return n - Math.floor(n);
|
||
}
|
||
|
||
export { generated, surfaceGyAt, genColumn, regenerateVisibleChunks, ensureGenAroundCamera, seededRandom }; |