grechka-game/src/world/generation.js

126 lines
4.6 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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 };