From 466e80aa232cb5c3dfe0872baeca504fc0f0f2dd Mon Sep 17 00:00:00 2001 From: cha0s Date: Wed, 10 Jul 2024 14:59:07 -0500 Subject: [PATCH] fun: generate a forest --- app/create-forest.js | 147 +++++++++++++++++++++++++++++++++++++++ app/create-player.js | 2 +- app/engine.js | 6 ++ app/net/server/worker.js | 13 ++++ 4 files changed, 167 insertions(+), 1 deletion(-) create mode 100644 app/create-forest.js diff --git a/app/create-forest.js b/app/create-forest.js new file mode 100644 index 0000000..4ed7dd0 --- /dev/null +++ b/app/create-forest.js @@ -0,0 +1,147 @@ +import alea from 'alea'; +import {createNoise2D} from 'simplex-noise'; + +import {createRandom, Generator} from '@/util/math.js'; + +import createEcs from './create-ecs.js'; + +const seed = 42069; +const prng = alea(seed); +const rawNoise = createNoise2D(prng); +const noise = (x, y) => (1 + rawNoise(x, y)) / 2; +const random = createRandom(seed); + +const indexComputer = (indices, randomizer) => ( + (position) => indices[Math.floor(indices.length * randomizer(position))] +); + +const [w, h] = [256, 256]; + +const Forest = new Generator({ + calculate: indexComputer( + [1, 3, 4, 10, 11, 12], // grass + ({x, y}) => noise(x * 30, y * 30), + ), + covers: () => true, + size: {w, h}, + children: [ + new Generator({ + calculate: indexComputer( + [342, 456], // dirt + ({x, y}) => noise(x * 30, y * 30), + ), + covers: ({x, y}) => noise(x * (1 / 16), y * (1 / 16)) < 0.5, + size: {w, h}, + children: [ + new Generator({ + calculate: indexComputer( + [407, 408, 423, 424], // stone path + ({x, y}) => noise(x * 30, y * 30), + ), + covers: ({x, y}) => noise(x * (1 / 16), y * (1 / 16)) < 0.1625, + size: {w, h}, + }), + ], + }), + new Generator({ + calculate: indexComputer( + [103, 104], // water + ({x, y}) => noise(x * 30, y * 30), + ), + covers: ({x, y}) => noise(x * (1 / 32), y * (1 / 32)) < 0.3, + size: {w, h}, + }), + ], +}); + +export default async function createForest(Ecs) { + const ecs = createEcs(Ecs); + const area = {x: w, y: h}; + const master = ecs.get(await ecs.create({ + AreaSize: {x: area.x * 16, y: area.y * 16}, + Ticking: {}, + TileLayers: { + layers: [ + { + area, + data: Array(w * h).fill(0), + source: '/assets/tileset.json', + tileSize: {x: 16, y: 16}, + }, + { + area, + data: Array(w * h).fill(0), + source: '/assets/tileset.json', + tileSize: {x: 16, y: 16}, + }, + ], + }, + Time: {}, + })); + Forest.generate(); + const layer0 = master.TileLayers.layers[0]; + layer0.data = Forest.matrix; + for (let y = 0; y < h; ++y) { + for (let x = 0; x < w; ++x) { + const dirt = Forest.children[0].matrix[y * w + x]; + if (dirt) { + layer0.data[y * w + x] = dirt; + } + const stone = Forest.children[0].children[0].matrix[y * w + x]; + if (stone) { + layer0.data[y * w + x] = stone; + } + const water = Forest.children[1].matrix[y * w + x]; + if (water) { + layer0.data[y * w + x] = water; + } + } + } + + const entityPosition = (x, y) => ({ + x: (8 + (x * 16) + random() * 8 - 4), + y: (8 + (y * 16) + random() * 8 - 4), + }); + + for (let y = 0; y < h; ++y) { + for (let x = 0; x < w; ++x) { + let v = noise(x * (1 / 24), y * (1 / 24)); + if (v > 0.2) { + v = noise(x * (1 / 7), y * (1 / 7)); + if (v < 0.15) { + await ecs.create({ + Position: entityPosition(x, y), + Sprite: { + anchorY: 0.7, + source: '/assets/ambient/shrub.json', + }, + VisibleAabb: {}, + }); + } + else if (v < 0.17) { + await ecs.create({ + Position: entityPosition(x, y), + Sprite: { + anchorY: 0.875, + source: '/assets/ambient/tree.json', + }, + VisibleAabb: {}, + }); + } + v = noise(x * (1 / 12), y * (1 / 12)); + if (v < 0.08) { + await ecs.create({ + Position: entityPosition(x, y), + Sprite: { + anchorY: 0.7, + source: '/assets/ambient/flower.json', + }, + VisibleAabb: {}, + }); + } + } + } + } + return ecs; +} + diff --git a/app/create-player.js b/app/create-player.js index 5b90e87..1723be2 100644 --- a/app/create-player.js +++ b/app/create-player.js @@ -15,7 +15,7 @@ export default async function createPlayer(id) { }, Controlled: {}, Direction: {direction: 2}, - Ecs: {path: ['homesteads', `${id}`].join('/')}, + Ecs: {path: ['forests', `${id}`].join('/')}, Emitter: {}, Forces: {}, Interacts: {}, diff --git a/app/engine.js b/app/engine.js index 390ac61..d4e0075 100644 --- a/app/engine.js +++ b/app/engine.js @@ -7,6 +7,7 @@ import {decode, encode} from '@/packets/index.js'; import Script from '@/util/script.js'; import createEcs from './create-ecs.js'; +import createForest from './create-forest.js'; import createHomestead from './create-homestead.js'; import createHouse from './create-house.js'; import createPlayer from './create-player.js'; @@ -260,6 +261,11 @@ export default class Engine { ['houses', `${id}`].join('/'), house, ); + const forest = await createForest(this.Ecs, id); + await this.saveEcs( + ['forests', `${id}`].join('/'), + forest, + ); buffer = await createPlayer(id); await this.server.writeData( ['players', `${id}`].join('/'), diff --git a/app/net/server/worker.js b/app/net/server/worker.js index 1d0ee45..847de92 100644 --- a/app/net/server/worker.js +++ b/app/net/server/worker.js @@ -2,6 +2,7 @@ import {del, get, set} from 'idb-keyval'; import {encode} from '@/packets/index.js'; +import '../../create-forest.js'; import '../../create-homestead.js'; import '../../create-player.js'; @@ -92,6 +93,18 @@ if (import.meta.hot) { // await engine.server.writeData('players/0', (new TextEncoder()).encode(JSON.stringify(player))); resolver.resolve(); }); + import.meta.hot.accept('../../create-forest.js', async ({default: createForest}) => { + const resolver = createResolver(); + resolvers.push(resolver); + await beforeResolver; + delete engine.ecses['forests/0']; + await engine.server.removeData('forests/0'); + const last = performance.now(); + const forest = await createForest(engine.Ecs, '0'); + console.log((performance.now() - last) / 1000); + await engine.saveEcs('forests/0', forest); + resolver.resolve(); + }); import.meta.hot.accept('../../create-homestead.js', async ({default: createHomestead}) => { const resolver = createResolver(); resolvers.push(resolver);