2021-05-04 17:41:30 +00:00
|
|
|
#include "zpl.h"
|
|
|
|
|
|
|
|
#include <math.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
|
|
|
#include "world/world.h"
|
|
|
|
#include "world/blocks.h"
|
|
|
|
#include "world/perlin.h"
|
|
|
|
|
2022-09-27 12:56:48 +00:00
|
|
|
#include "ecs/components.h"
|
|
|
|
#include "ents/entity.h"
|
|
|
|
#include "ents/vehicle.h"
|
|
|
|
#include "ents/items.h"
|
2021-11-02 11:49:03 +00:00
|
|
|
#include "world/blocks_info.h"
|
2021-09-08 14:12:38 +00:00
|
|
|
|
2021-11-03 18:04:34 +00:00
|
|
|
#define WORLD_BLOCK_OBSERVER(name) block_id name(block_id *data, block_id id, uint32_t block_idx)
|
2021-05-04 17:41:30 +00:00
|
|
|
typedef WORLD_BLOCK_OBSERVER(world_block_observer_proc);
|
|
|
|
|
2021-07-26 19:06:49 +00:00
|
|
|
#define WORLD_PERLIN_FREQ 100
|
|
|
|
#define WORLD_PERLIN_OCTAVES 1
|
2021-05-04 17:41:30 +00:00
|
|
|
|
2021-11-02 11:49:03 +00:00
|
|
|
#define BLOCK_INVALID 0xF
|
|
|
|
|
2022-09-28 20:01:47 +00:00
|
|
|
int eco_incircle(int x, int y, int radius) {
|
|
|
|
return (zpl_pow(x, 2) + zpl_pow(y, 2)) < zpl_pow(radius, 2);
|
|
|
|
}
|
|
|
|
|
2021-11-03 18:04:34 +00:00
|
|
|
block_id worldgen_biome_find(uint32_t biome, uint32_t kind) {
|
2021-11-02 11:49:03 +00:00
|
|
|
asset_id asset = ASSET_INVALID;
|
|
|
|
switch (biome) {
|
|
|
|
case BLOCK_BIOME_DEV: {
|
|
|
|
switch (kind) {
|
|
|
|
case BLOCK_KIND_GROUND: asset = ASSET_GROUND; break;
|
|
|
|
case BLOCK_KIND_DIRT: asset = ASSET_DIRT; break;
|
|
|
|
case BLOCK_KIND_WALL: asset = ASSET_WALL; break;
|
|
|
|
case BLOCK_KIND_HILL_SNOW:
|
|
|
|
case BLOCK_KIND_HILL: asset = ASSET_HILL; break;
|
|
|
|
case BLOCK_KIND_WATER: asset = ASSET_WATER; break;
|
|
|
|
case BLOCK_KIND_LAVA: asset = ASSET_LAVA; break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2022-09-28 20:01:47 +00:00
|
|
|
|
2021-11-02 11:49:03 +00:00
|
|
|
return blocks_find(asset);
|
|
|
|
}
|
|
|
|
|
2021-05-12 14:02:12 +00:00
|
|
|
static world_data *world;
|
|
|
|
|
2021-11-03 18:04:34 +00:00
|
|
|
static void world_fill_rect(block_id *data, block_id id, uint32_t x, uint32_t y, uint32_t w, uint32_t h, world_block_observer_proc *proc) {
|
2021-05-04 17:41:30 +00:00
|
|
|
for (uint32_t cy=y; cy<y+h; cy++) {
|
|
|
|
for (uint32_t cx=x; cx<x+w; cx++) {
|
2021-05-12 14:02:12 +00:00
|
|
|
if (cx < 0 || cx >= world->dim) continue;
|
|
|
|
if (cy < 0 || cy >= world->dim) continue;
|
|
|
|
uint32_t i = (cy*world->dim) + cx;
|
2022-09-28 20:01:47 +00:00
|
|
|
|
2021-05-04 17:41:30 +00:00
|
|
|
if (proc) {
|
2021-11-03 18:04:34 +00:00
|
|
|
block_id new_id = (*proc)(data, id, i);
|
2021-05-04 17:41:30 +00:00
|
|
|
if (new_id != BLOCK_INVALID) {
|
|
|
|
id = new_id;
|
|
|
|
}
|
|
|
|
else continue;
|
|
|
|
}
|
2022-09-28 20:01:47 +00:00
|
|
|
|
2021-11-02 17:09:54 +00:00
|
|
|
data[i] = id;
|
2021-05-04 17:41:30 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-09-28 20:01:47 +00:00
|
|
|
static void world_fill_circle(block_id *data, block_id id, uint32_t cx, uint32_t cy, uint32_t radius, world_block_observer_proc *proc) {
|
|
|
|
for (int x = -(int32_t)(radius); x < (int32_t)radius; ++x) {
|
|
|
|
for (int y = -(int32_t)(radius); y < (int32_t)radius; ++y) {
|
|
|
|
if (eco_incircle(x, y, radius)) {
|
|
|
|
int fx = x + cx;
|
|
|
|
int fy = y + cy;
|
|
|
|
|
|
|
|
uint32_t i = (fy*world->dim) + fx;
|
|
|
|
|
|
|
|
if (proc) {
|
|
|
|
block_id new_id = (*proc)(data, id, i);
|
|
|
|
if (new_id != BLOCK_INVALID) {
|
|
|
|
id = new_id;
|
|
|
|
}
|
|
|
|
else continue;
|
2021-05-04 17:41:30 +00:00
|
|
|
}
|
2022-09-28 20:01:47 +00:00
|
|
|
|
|
|
|
data[i] = id;
|
2021-05-04 17:41:30 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-03 18:04:34 +00:00
|
|
|
static void world_fill_rect_anchor(block_id *data, block_id id, uint32_t x, uint32_t y, uint32_t w, uint32_t h, float ax, float ay, world_block_observer_proc *proc) {
|
2021-05-04 17:41:30 +00:00
|
|
|
uint32_t w2 = (uint32_t)floorf(w*ax);
|
|
|
|
uint32_t h2 = (uint32_t)floorf(h*ay);
|
2021-11-02 17:09:54 +00:00
|
|
|
world_fill_rect(data, id, x-w2, y-h2, w, h, proc);
|
2021-05-04 17:41:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static WORLD_BLOCK_OBSERVER(shaper) {
|
2021-11-02 11:49:03 +00:00
|
|
|
uint32_t kind = id;
|
2021-11-02 17:09:54 +00:00
|
|
|
uint32_t old_kind = data[block_idx];
|
2022-09-28 20:01:47 +00:00
|
|
|
|
2021-11-02 11:49:03 +00:00
|
|
|
if (kind == BLOCK_KIND_WALL && kind == old_kind) {
|
|
|
|
return worldgen_biome_find(BLOCK_BIOME_DEV, BLOCK_KIND_HILL);
|
|
|
|
}
|
|
|
|
if (kind == BLOCK_KIND_HILL && kind == old_kind) {
|
|
|
|
return worldgen_biome_find(BLOCK_BIOME_DEV, BLOCK_KIND_HILL_SNOW);
|
2021-05-04 17:41:30 +00:00
|
|
|
}
|
2022-09-28 20:01:47 +00:00
|
|
|
|
2021-05-04 17:41:30 +00:00
|
|
|
return id;
|
|
|
|
}
|
|
|
|
|
2021-11-03 18:04:34 +00:00
|
|
|
static block_id world_perlin_cond_offset(uint32_t block_idx, double chance, uint32_t ofx, uint32_t ofy) {
|
2021-11-02 17:09:54 +00:00
|
|
|
uint32_t x = block_idx % world->dim + ofx;
|
|
|
|
uint32_t y = block_idx / world->dim + ofy;
|
2022-09-28 20:01:47 +00:00
|
|
|
|
2021-07-26 19:06:49 +00:00
|
|
|
return perlin_fbm(world->seed, x, y, WORLD_PERLIN_FREQ, WORLD_PERLIN_OCTAVES) < chance;
|
2021-05-04 17:41:30 +00:00
|
|
|
}
|
|
|
|
|
2021-11-03 18:04:34 +00:00
|
|
|
static block_id world_perlin_cond(uint32_t block_idx, double chance) {
|
2021-11-02 17:09:54 +00:00
|
|
|
return world_perlin_cond_offset(block_idx, chance, 0, 0);
|
|
|
|
}
|
|
|
|
|
2021-07-19 07:56:15 +00:00
|
|
|
#if 1
|
2021-05-04 17:41:30 +00:00
|
|
|
static WORLD_BLOCK_OBSERVER(shaper_noise80) {
|
2021-11-02 17:09:54 +00:00
|
|
|
return world_perlin_cond(block_idx, 0.80) ? shaper(data, id, block_idx) : BLOCK_INVALID;
|
2021-05-04 17:41:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static WORLD_BLOCK_OBSERVER(shaper_noise50) {
|
2021-11-02 17:09:54 +00:00
|
|
|
return world_perlin_cond(block_idx, 0.50) ? shaper(data, id, block_idx) : BLOCK_INVALID;
|
2021-05-04 17:41:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static WORLD_BLOCK_OBSERVER(shaper_noise33) {
|
2021-11-02 17:09:54 +00:00
|
|
|
return world_perlin_cond(block_idx, 0.33) ? shaper(data, id, block_idx) : BLOCK_INVALID;
|
2021-05-04 17:41:30 +00:00
|
|
|
}
|
2021-08-10 11:19:45 +00:00
|
|
|
|
|
|
|
static WORLD_BLOCK_OBSERVER(shaper_noise05) {
|
2021-11-02 17:09:54 +00:00
|
|
|
return world_perlin_cond(block_idx, 0.05) ? shaper(data, id, block_idx) : BLOCK_INVALID;
|
2021-08-10 11:19:45 +00:00
|
|
|
}
|
2021-11-02 17:09:54 +00:00
|
|
|
|
|
|
|
static WORLD_BLOCK_OBSERVER(shaper_noise05b) {
|
|
|
|
return world_perlin_cond_offset(block_idx, 0.05, 32, 0) ? shaper(data, id, block_idx) : BLOCK_INVALID;
|
|
|
|
}
|
|
|
|
|
2022-09-14 08:02:35 +00:00
|
|
|
static WORLD_BLOCK_OBSERVER(shaper_noise01b) {
|
|
|
|
return world_perlin_cond_offset(block_idx, 0.01, 32, 0) ? shaper(data, id, block_idx) : BLOCK_INVALID;
|
|
|
|
}
|
2021-07-19 07:56:15 +00:00
|
|
|
#else
|
|
|
|
static WORLD_BLOCK_OBSERVER(shaper_noise80) {
|
|
|
|
return rand()%10 < 8 ? shaper(id, block_idx) : BLOCK_INVALID;
|
|
|
|
}
|
|
|
|
|
|
|
|
static WORLD_BLOCK_OBSERVER(shaper_noise50) {
|
|
|
|
return rand()%10 < 5 ? shaper(id, block_idx) : BLOCK_INVALID;
|
|
|
|
}
|
|
|
|
|
|
|
|
static WORLD_BLOCK_OBSERVER(shaper_noise33) {
|
|
|
|
return rand()%10 < 3 ? shaper(id, block_idx) : BLOCK_INVALID;
|
|
|
|
}
|
|
|
|
#endif
|
2021-05-04 17:41:30 +00:00
|
|
|
|
2021-05-10 09:35:04 +00:00
|
|
|
#if 0
|
2021-05-04 17:41:30 +00:00
|
|
|
static void world_fill_mountain(uint32_t x, uint32_t y) {
|
2022-09-28 20:01:47 +00:00
|
|
|
|
2021-05-04 17:41:30 +00:00
|
|
|
}
|
2021-05-10 09:35:04 +00:00
|
|
|
#endif
|
2021-05-04 17:41:30 +00:00
|
|
|
|
|
|
|
#define RAND_RANGE(x,y) (x + (int)rand()%(y-(x)))
|
2021-11-02 17:09:54 +00:00
|
|
|
#define RAND_RANGEF(x,y) ((float)RAND_RANGE(x,y))
|
2021-05-04 17:41:30 +00:00
|
|
|
|
2022-09-28 05:29:32 +00:00
|
|
|
int32_t worldgen_build(world_data *wld) {
|
2021-05-12 14:02:12 +00:00
|
|
|
// TODO(zaklaus): pass world as an arg instead
|
|
|
|
world = wld;
|
2022-09-28 20:01:47 +00:00
|
|
|
|
2021-05-04 17:41:30 +00:00
|
|
|
// TODO: perform world gen
|
|
|
|
// atm, we will fill the world with ground and surround it by walls
|
2021-11-03 18:04:34 +00:00
|
|
|
block_id wall_id = worldgen_biome_find(BLOCK_BIOME_DEV, BLOCK_KIND_WALL);
|
|
|
|
block_id grnd_id = worldgen_biome_find(BLOCK_BIOME_DEV, BLOCK_KIND_GROUND);
|
|
|
|
block_id dirt_id = worldgen_biome_find(BLOCK_BIOME_DEV, BLOCK_KIND_DIRT);
|
|
|
|
block_id watr_id = worldgen_biome_find(BLOCK_BIOME_DEV, BLOCK_KIND_WATER);
|
|
|
|
block_id lava_id = worldgen_biome_find(BLOCK_BIOME_DEV, BLOCK_KIND_LAVA);
|
|
|
|
block_id tree_id = blocks_find(ASSET_TREE);
|
2022-09-28 20:01:47 +00:00
|
|
|
|
2021-05-12 14:02:12 +00:00
|
|
|
srand(world->seed);
|
2022-09-28 20:01:47 +00:00
|
|
|
|
2021-05-04 17:41:30 +00:00
|
|
|
// walls
|
2021-11-02 17:09:54 +00:00
|
|
|
world_fill_rect(world->data, wall_id, 0, 0, world->dim, world->dim, NULL);
|
2022-09-28 20:01:47 +00:00
|
|
|
|
2021-05-04 17:41:30 +00:00
|
|
|
// ground
|
2022-09-28 20:01:47 +00:00
|
|
|
world_fill_rect(world->data, watr_id, 1, 1, world->dim-2, world->dim-2, NULL);
|
|
|
|
|
|
|
|
int radius = 25;
|
|
|
|
|
|
|
|
// wide boy circle
|
|
|
|
world_fill_circle(world->data, dirt_id, world->dim / 2, world->dim / 2, radius, NULL);
|
|
|
|
|
|
|
|
// narrow boy cirlce
|
|
|
|
world_fill_circle(world->data, grnd_id, world->dim / 2, world->dim / 2, radius * 0.7f, NULL);
|
|
|
|
|
|
|
|
|
2021-05-04 17:41:30 +00:00
|
|
|
return WORLD_ERROR_NONE;
|
|
|
|
}
|