[RELEASE] u22-v03

This version is submitted to U22 breau.
This commit is contained in:
2020-09-14 00:00:00 +00:00
parent 360595de37
commit 84c3a02b9a
357 changed files with 29223 additions and 0 deletions

View File

@@ -0,0 +1,27 @@
add_library(loworld
chunk.c
environment.c
generator.c
poolset.c
store.c
template.c
view.c
)
target_link_libraries(loworld
msgpackc
chaos
container
jukebox
math
memory
mpkutil
lobullet
locharacter
locommon
loentity
loground
loresource
loshader
)

159
core/loworld/chunk.c Normal file
View File

@@ -0,0 +1,159 @@
#include "./chunk.h"
#include <assert.h>
#include <inttypes.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include <msgpack.h>
#include "util/container/array.h"
#include "util/mpkutil/get.h"
#include "util/mpkutil/pack.h"
#include "core/loentity/entity.h"
#include "./poolset.h"
static void loworld_chunk_pack_entities_(
const loworld_chunk_t* chunk, msgpack_packer* packer) {
assert(chunk != NULL);
assert(packer != NULL);
const size_t len = container_array_get_length(chunk->entities);
msgpack_pack_array(packer, len);
for (size_t i = 0; i < len; ++i) {
loentity_pack(chunk->entities[i], packer);
}
}
const char* loworld_chunk_biome_stringify(loworld_chunk_biome_t biome) {
# define each_(NAME, name) \
if (biome == LOWORLD_CHUNK_BIOME_##NAME) return #name;
LOWORLD_CHUNK_BIOME_EACH_(each_);
assert(false);
return NULL;
# undef each_
}
bool loworld_chunk_biome_unstringify(
loworld_chunk_biome_t* biome, const char* str, size_t len) {
assert(biome != NULL);
assert(str != NULL || len == 0);
# define each_(NAME, name) do { \
if (strncmp(str, #name, len) == 0 && #name[len] == 0) { \
*biome = LOWORLD_CHUNK_BIOME_##NAME; \
return true; \
} \
} while (0)
LOWORLD_CHUNK_BIOME_EACH_(each_);
return false;
# undef each_
}
void loworld_chunk_initialize(loworld_chunk_t* chunk) {
assert(chunk != NULL);
*chunk = (typeof(*chunk)) {0};
}
void loworld_chunk_deinitialize(loworld_chunk_t* chunk) {
if (chunk == NULL) return;
loworld_chunk_clear(chunk);
container_array_delete(chunk->entities);
}
void loworld_chunk_add_entity(loworld_chunk_t* chunk, loentity_t* entity) {
assert(chunk != NULL);
assert(entity != NULL);
const size_t index = container_array_get_length(chunk->entities);
container_array_insert(chunk->entities, index);
chunk->entities[index] = entity;
}
void loworld_chunk_clear(loworld_chunk_t* chunk) {
assert(chunk != NULL);
const size_t len = container_array_get_length(chunk->entities);
for (size_t i = 0; i < len; ++i) {
loentity_delete(chunk->entities[i]);
}
container_array_resize(chunk->entities, 0);
}
void loworld_chunk_pack(const loworld_chunk_t* chunk, msgpack_packer* packer) {
assert(chunk != NULL);
assert(packer != NULL);
msgpack_pack_map(packer, 3);
mpkutil_pack_str(packer, "pos");
msgpack_pack_array(packer, 2);
msgpack_pack_int32(packer, chunk->pos.x);
msgpack_pack_int32(packer, chunk->pos.y);
mpkutil_pack_str(packer, "biome");
mpkutil_pack_str(packer, loworld_chunk_biome_stringify(chunk->biome));
mpkutil_pack_str(packer, "entities");
loworld_chunk_pack_entities_(chunk, packer);
}
bool loworld_chunk_unpack(
loworld_chunk_t* chunk,
const msgpack_object* obj,
const loworld_poolset_t* pools) {
assert(chunk != NULL);
assert(obj != NULL);
assert(pools != NULL);
const msgpack_object_map* root = mpkutil_get_map(obj);
if (root == NULL) return false;
#define item_(name) mpkutil_get_map_item_by_str(root, name)
const msgpack_object_array* pos = mpkutil_get_array(item_("pos"));
if (pos == NULL || pos->size != 2 ||
!mpkutil_get_int32(&pos->ptr[0], &chunk->pos.x) ||
!mpkutil_get_int32(&pos->ptr[1], &chunk->pos.y)) {
return false;
}
const char* biome;
size_t biome_len;
if (!mpkutil_get_str(item_("biome"), &biome, &biome_len) ||
!loworld_chunk_biome_unstringify(&chunk->biome, biome, biome_len)) {
return false;
}
const msgpack_object_array* entities = mpkutil_get_array(item_("entities"));
if (entities != NULL) {
container_array_reserve(chunk->entities, entities->size);
for (size_t i = 0; i < entities->size; ++i) {
loentity_t* e = loworld_poolset_unpack_entity(pools, &entities->ptr[i]);
if (e != NULL) loworld_chunk_add_entity(chunk, e);
}
}
#undef item_
return true;
}
void loworld_chunk_build_filename(
const loworld_chunk_t* chunk, char* filename, size_t length) {
assert(chunk != NULL);
assert(filename != NULL || length == 0);
snprintf(filename, length,
"%"PRId32"_%"PRId32".msgpack", chunk->pos.x, chunk->pos.y);
}

98
core/loworld/chunk.h Normal file
View File

@@ -0,0 +1,98 @@
#pragma once
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <msgpack.h>
#include "util/container/array.h"
#include "core/loentity/entity.h"
#include "./poolset.h"
#define LOWORLD_CHUNK_FILENAME_MAX 64
/* dont forget to change EACH macro */
typedef enum {
LOWORLD_CHUNK_BIOME_METAPHYSICAL_GATE,
LOWORLD_CHUNK_BIOME_CAVIAS_CAMP,
LOWORLD_CHUNK_BIOME_LABORATORY,
LOWORLD_CHUNK_BIOME_BOSS_THEISTS_CHILD,
LOWORLD_CHUNK_BIOME_BOSS_BIG_WARDER,
LOWORLD_CHUNK_BIOME_BOSS_GREEDY_SCIENTIST,
} loworld_chunk_biome_t;
#define LOWORLD_CHUNK_BIOME_EACH_(PROC) do { \
PROC(METAPHYSICAL_GATE, metaphysical-gate); \
PROC(CAVIAS_CAMP, cavias-camp); \
PROC(LABORATORY, laboratory); \
PROC(BOSS_THEISTS_CHILD, boss-theists-child); \
PROC(BOSS_BIG_WARDER, boss-big-warder); \
PROC(BOSS_GREEDY_SCIENTIST, boss-greedy-scientist); \
} while (0)
typedef struct {
struct {
int32_t x;
int32_t y;
} pos;
loworld_chunk_biome_t biome;
CONTAINER_ARRAY loentity_t** entities;
} loworld_chunk_t;
const char*
loworld_chunk_biome_stringify(
loworld_chunk_biome_t biome
);
bool
loworld_chunk_biome_unstringify(
loworld_chunk_biome_t* biome,
const char* str,
size_t len
);
void
loworld_chunk_initialize(
loworld_chunk_t* chunk
);
void
loworld_chunk_deinitialize(
loworld_chunk_t* chunk
);
void
loworld_chunk_add_entity(
loworld_chunk_t* chunk,
loentity_t* entity /* OWNERSHIP */
);
void
loworld_chunk_clear(
loworld_chunk_t* chunk
);
void
loworld_chunk_pack(
const loworld_chunk_t* chunk,
msgpack_packer* packer
);
bool
loworld_chunk_unpack(
loworld_chunk_t* chunk, /* should be cleared */
const msgpack_object* obj,
const loworld_poolset_t* pools
);
void
loworld_chunk_build_filename(
const loworld_chunk_t* chunk,
char* filename,
size_t length
);

254
core/loworld/environment.c Normal file
View File

@@ -0,0 +1,254 @@
#include "./environment.h"
#include <assert.h>
#include <stddef.h>
#include <stdint.h>
#include "util/jukebox/amp.h"
#include "util/jukebox/decoder.h"
#include "util/math/rational.h"
#include "core/locommon/easing.h"
#include "core/locommon/ticker.h"
#include "core/loplayer/event.h"
#include "core/loplayer/player.h"
#include "core/loresource/music.h"
#include "core/loresource/set.h"
#include "core/loresource/text.h"
#include "core/loshader/set.h"
#include "core/loshader/backwall.h"
#include "core/loshader/fog.h"
#include "./view.h"
static const char* loworld_environment_get_chunk_name_(
loresource_language_t lang, loworld_chunk_biome_t b) {
switch (b) {
case LOWORLD_CHUNK_BIOME_METAPHYSICAL_GATE:
return loresource_text_get(lang, "biome_metaphysical_gate");
case LOWORLD_CHUNK_BIOME_CAVIAS_CAMP:
return loresource_text_get(lang, "biome_cavias_camp");
case LOWORLD_CHUNK_BIOME_LABORATORY:
return loresource_text_get(lang, "biome_laboratory");
case LOWORLD_CHUNK_BIOME_BOSS_THEISTS_CHILD:
return loresource_text_get(lang, "biome_boss_theists_child");
case LOWORLD_CHUNK_BIOME_BOSS_BIG_WARDER:
return loresource_text_get(lang, "biome_boss_big_warder");
case LOWORLD_CHUNK_BIOME_BOSS_GREEDY_SCIENTIST:
return loresource_text_get(lang, "biome_boss_greedy_scientist");
}
assert(false);
return "unknown biome";
}
static loshader_backwall_type_t loworld_environment_get_backwall_type_(
loworld_chunk_biome_t b) {
switch (b) {
case LOWORLD_CHUNK_BIOME_METAPHYSICAL_GATE:
return LOSHADER_BACKWALL_TYPE_INFINITE_BOXES;
case LOWORLD_CHUNK_BIOME_CAVIAS_CAMP:
return LOSHADER_BACKWALL_TYPE_HOLLOW_MOUNTAINS;
case LOWORLD_CHUNK_BIOME_LABORATORY:
return LOSHADER_BACKWALL_TYPE_FABRIC;
case LOWORLD_CHUNK_BIOME_BOSS_THEISTS_CHILD:
return LOSHADER_BACKWALL_TYPE_HOLLOW_MOUNTAINS_RED;
case LOWORLD_CHUNK_BIOME_BOSS_BIG_WARDER:
return LOSHADER_BACKWALL_TYPE_JAIL;
case LOWORLD_CHUNK_BIOME_BOSS_GREEDY_SCIENTIST:
return LOSHADER_BACKWALL_TYPE_INFINITE_BOXES;
}
assert(false);
return LOSHADER_BACKWALL_TYPE_WHITE;
}
static loshader_fog_type_t loworld_environment_get_fog_type_(
loworld_chunk_biome_t b) {
switch (b) {
case LOWORLD_CHUNK_BIOME_METAPHYSICAL_GATE:
case LOWORLD_CHUNK_BIOME_CAVIAS_CAMP:
case LOWORLD_CHUNK_BIOME_LABORATORY:
case LOWORLD_CHUNK_BIOME_BOSS_THEISTS_CHILD:
case LOWORLD_CHUNK_BIOME_BOSS_BIG_WARDER:
case LOWORLD_CHUNK_BIOME_BOSS_GREEDY_SCIENTIST:
return LOSHADER_FOG_TYPE_WHITE_CLOUD;
}
assert(false);
return LOSHADER_FOG_TYPE_NONE;
}
static loresource_music_player_t* loworld_environment_get_music_(
loworld_chunk_biome_t b, loresource_music_t* m) {
switch (b) {
case LOWORLD_CHUNK_BIOME_METAPHYSICAL_GATE:
return &m->biome_metaphysical_gate;
case LOWORLD_CHUNK_BIOME_CAVIAS_CAMP:
return &m->biome_cavias_camp;
case LOWORLD_CHUNK_BIOME_LABORATORY:
return &m->biome_laboratory;
case LOWORLD_CHUNK_BIOME_BOSS_THEISTS_CHILD:
case LOWORLD_CHUNK_BIOME_BOSS_BIG_WARDER:
case LOWORLD_CHUNK_BIOME_BOSS_GREEDY_SCIENTIST:
return &m->biome_boss;
}
assert(false);
return NULL;
}
static void loworld_environment_stop_music_(loworld_environment_t* env) {
assert(env != NULL);
if (env->music != NULL && env->music_control) {
jukebox_amp_change_volume(&env->music->amp, 0, &rational(1, 1));
jukebox_decoder_stop_after(env->music->decoder, &rational(1, 1));
}
env->music = NULL;
}
static void loworld_environment_update_hud_(loworld_environment_t* env) {
assert(env != NULL);
if (env->transition > 0) return;
loplayer_hud_set_biome_text(
env->player->hud,
loworld_environment_get_chunk_name_(env->res->lang, env->biome));
}
static void loworld_environment_update_backwall_(loworld_environment_t* env) {
assert(env != NULL);
loshader_backwall_type_t prev = env->backwall.prev_type;
if (env->transition == 0) prev = env->backwall.type;
env->backwall = (loshader_backwall_drawer_param_t) {
.prev_type = prev,
.type = LOSHADER_BACKWALL_TYPE_WHITE,
.transition = env->transition,
};
if (!env->config.disable_heavy_backwall) {
env->backwall.type = loworld_environment_get_backwall_type_(env->biome);
}
}
static void loworld_environment_update_fog_(loworld_environment_t* env) {
assert(env != NULL);
if (env->transition == 0) env->fog.prev_type = env->fog.type;
env->fog.type = LOSHADER_FOG_TYPE_NONE;
if (!env->config.disable_heavy_fog) {
env->fog.type = loworld_environment_get_fog_type_(env->biome);
}
env->fog.transition = env->transition;
/* ---- bounds fog ---- */
const loplayer_event_param_t* e =
loplayer_event_get_param(env->player->event);
if (e != NULL && vec2_pow_length(&e->area_size) > 0) {
env->fog.bounds_pos = e->area_pos;
env->fog.bounds_size = e->area_size;
locommon_easing_smooth_float(
&env->fog.bounds_fog, 1, env->ticker->delta_f);
} else {
locommon_easing_smooth_float(
&env->fog.bounds_fog, 0, env->ticker->delta_f);
}
}
static void loworld_environment_update_music_(loworld_environment_t* env) {
assert(env != NULL);
bool control = true;
loresource_music_player_t* music =
loworld_environment_get_music_(env->biome, &env->res->music);
const loplayer_event_param_t* e =
loplayer_event_get_param(env->player->event);
if (e != NULL) {
music = e->music;
control = false;
if (!env->sound_attenuation) {
loresource_sound_change_master_volume(
env->res->sound, .2f, &rational(1, 1));
env->sound_attenuation = true;
}
} else {
if (env->sound_attenuation) {
loresource_sound_change_master_volume(
env->res->sound, 1, &rational(1, 1));
env->sound_attenuation = false;
}
}
if (music != env->music) {
loworld_environment_stop_music_(env);
env->music = music;
env->music_control = control;
if (env->music != NULL && env->music_control) {
jukebox_amp_change_volume(&env->music->amp, .6f, &rational(1, 1));
jukebox_decoder_resume(env->music->decoder, true);
}
}
}
void loworld_environment_initialize(
loworld_environment_t* env,
loresource_set_t* res,
loshader_set_t* shaders,
const locommon_ticker_t* ticker,
const loworld_view_t* view,
loplayer_t* player,
const loworld_environment_config_t* config) {
assert(env != NULL);
assert(res != NULL);
assert(shaders != NULL);
assert(ticker != NULL);
assert(view != NULL);
assert(player != NULL);
assert(config != NULL);
*env = (typeof(*env)) {
.res = res,
.shaders = shaders,
.ticker = ticker,
.view = view,
.player = player,
.config = *config,
};
}
void loworld_environment_deinitialize(loworld_environment_t* env) {
assert(env != NULL);
loworld_environment_stop_music_(env);
}
void loworld_environment_update(loworld_environment_t* env) {
assert(env != NULL);
const loworld_chunk_t* chunk = loworld_view_get_looking_chunk(env->view);
if (env->transition == 1 && env->biome != chunk->biome) {
env->biome = chunk->biome;
env->transition = 0;
}
loworld_environment_update_hud_(env);
loworld_environment_update_backwall_(env);
loworld_environment_update_fog_(env);
loworld_environment_update_music_(env);
locommon_easing_linear_float(&env->transition, 1, env->ticker->delta_f);
}
void loworld_environment_draw(const loworld_environment_t* env) {
assert(env != NULL);
loshader_backwall_drawer_set_param(
env->shaders->drawer.backwall, &env->backwall);
loshader_fog_drawer_set_param(
env->shaders->drawer.fog, &env->fog);
}

View File

@@ -0,0 +1,66 @@
#pragma once
#include <stdbool.h>
#include <stdint.h>
#include "core/locommon/ticker.h"
#include "core/loplayer/player.h"
#include "core/loresource/music.h"
#include "core/loresource/set.h"
#include "core/loshader/set.h"
#include "./view.h"
typedef struct {
bool disable_heavy_backwall;
bool disable_heavy_fog;
} loworld_environment_config_t;
typedef struct {
/* injected deps */
loresource_set_t* res;
loshader_set_t* shaders;
const locommon_ticker_t* ticker;
const loworld_view_t* view;
loplayer_t* player;
/* immutable params */
loworld_environment_config_t config;
/* read-only mutable params */
float transition;
loworld_chunk_biome_t biome;
loresource_music_player_t* music;
bool music_control;
bool sound_attenuation;
loshader_backwall_drawer_param_t backwall;
loshader_fog_drawer_param_t fog;
} loworld_environment_t;
void
loworld_environment_initialize(
loworld_environment_t* env,
loresource_set_t* res,
loshader_set_t* shaders,
const locommon_ticker_t* ticker,
const loworld_view_t* view,
loplayer_t* player,
const loworld_environment_config_t* config
);
void
loworld_environment_deinitialize(
loworld_environment_t* env
);
void
loworld_environment_update(
loworld_environment_t* env
);
void
loworld_environment_draw(
const loworld_environment_t* env
);

245
core/loworld/generator.c Normal file
View File

@@ -0,0 +1,245 @@
#include "./generator.h"
#include <stdbool.h>
#include <stdint.h>
#include <msgpack.h>
#include "util/chaos/xorshift.h"
#include "util/math/algorithm.h"
#include "util/memory/memory.h"
#include "util/mpkutil/get.h"
#include "./chunk.h"
#include "./poolset.h"
#include "./template.h"
struct loworld_generator_t {
/* injected deps */
const loworld_poolset_t* pools;
/* parameters */
uint64_t seed;
};
#define LOWORLD_GENERATOR_BLOCK_SIZE 8
static uint64_t loworld_generator_rand_(
const loworld_generator_t* gen, int32_t x, int32_t y) {
assert(gen != NULL);
const uint64_t ux = (uint64_t) x - INT32_MIN;
const uint64_t uy = (uint64_t) y - INT32_MIN;
/* multiply prime numbers */
return ux*400206850629133 + uy*890206850629189 + gen->seed;
}
static loworld_chunk_biome_t loworld_generator_decide_chunk_biome_(
const loworld_generator_t* gen, int32_t x, int32_t y) {
assert(gen != NULL);
static const loworld_chunk_biome_t base_biomes[] = {
LOWORLD_CHUNK_BIOME_CAVIAS_CAMP,
LOWORLD_CHUNK_BIOME_LABORATORY,
};
static const size_t base_biomes_length =
sizeof(base_biomes)/sizeof(base_biomes[0]);
/* random seed which depends the block */
uint64_t bs = loworld_generator_rand_(gen, x/LOWORLD_GENERATOR_BLOCK_SIZE, y);
const loworld_chunk_biome_t base =
base_biomes[(bs = chaos_xorshift(bs))%base_biomes_length];
uint64_t fx = x + (y == 0? 0: loworld_generator_rand_(gen, 0, y));
fx = MATH_ABS(fx)%LOWORLD_GENERATOR_BLOCK_SIZE;
if (fx == 0) return LOWORLD_CHUNK_BIOME_METAPHYSICAL_GATE;
if (fx == LOWORLD_GENERATOR_BLOCK_SIZE/2) {
switch (base) {
case LOWORLD_CHUNK_BIOME_CAVIAS_CAMP:
return LOWORLD_CHUNK_BIOME_BOSS_THEISTS_CHILD;
case LOWORLD_CHUNK_BIOME_LABORATORY:
return x%2?
LOWORLD_CHUNK_BIOME_BOSS_BIG_WARDER:
LOWORLD_CHUNK_BIOME_BOSS_GREEDY_SCIENTIST;
default: ;
}
}
return base;
}
static void loworld_generator_generate_for_metaphysical_gate_(
const loworld_generator_t* gen, loworld_chunk_t* chunk) {
assert(gen != NULL);
assert(chunk != NULL);
const loworld_template_building_param_t param = {
.target = chunk,
.pools = gen->pools,
.seed = loworld_generator_rand_(gen, chunk->pos.x, chunk->pos.y),
};
loworld_template_metaphysical_gate_build_chunk(&param);
}
static void loworld_generator_generate_for_cavias_camp_(
const loworld_generator_t* gen, loworld_chunk_t* chunk) {
assert(gen != NULL);
assert(chunk != NULL);
static void (*funcs[])(const loworld_template_building_param_t* p) = {
loworld_template_open_space_build_chunk,
loworld_template_broken_open_space_build_chunk,
loworld_template_passage_build_chunk,
loworld_template_broken_passage_build_chunk,
};
static const size_t funcs_len = sizeof(funcs)/sizeof(funcs[0]);
uint64_t s = loworld_generator_rand_(gen, chunk->pos.x, chunk->pos.y);
const uint64_t r = (s = chaos_xorshift(s))%funcs_len;
const loworld_template_building_param_t param = {
.target = chunk,
.pools = gen->pools,
.seed = chaos_xorshift(s),
};
return funcs[r](&param);
}
static void loworld_generator_generate_for_laboratory_(
const loworld_generator_t* gen, loworld_chunk_t* chunk) {
assert(gen != NULL);
assert(chunk != NULL);
static void (*funcs[])(const loworld_template_building_param_t* p) = {
loworld_template_passage_build_chunk,
loworld_template_broken_passage_build_chunk,
loworld_template_stairs_build_chunk,
};
static const size_t funcs_len = sizeof(funcs)/sizeof(funcs[0]);
uint64_t s = loworld_generator_rand_(gen, chunk->pos.x, chunk->pos.y);
const uint64_t r = (s = chaos_xorshift(s))%funcs_len;
const loworld_template_building_param_t param = {
.target = chunk,
.pools = gen->pools,
.seed = chaos_xorshift(s),
};
return funcs[r](&param);
}
static void loworld_generator_generate_for_boss_theists_child_(
const loworld_generator_t* gen, loworld_chunk_t* chunk) {
assert(gen != NULL);
assert(chunk != NULL);
const loworld_template_building_param_t param = {
.target = chunk,
.pools = gen->pools,
.seed = loworld_generator_rand_(gen, chunk->pos.x, chunk->pos.y),
};
loworld_template_boss_theists_child_build_chunk(&param);
}
static void loworld_generator_generate_for_boss_big_warder_(
const loworld_generator_t* gen, loworld_chunk_t* chunk) {
assert(gen != NULL);
assert(chunk != NULL);
const loworld_template_building_param_t param = {
.target = chunk,
.pools = gen->pools,
.seed = loworld_generator_rand_(gen, chunk->pos.x, chunk->pos.y),
};
loworld_template_boss_big_warder_build_chunk(&param);
}
static void loworld_generator_generate_for_boss_greedy_scientist_(
const loworld_generator_t* gen, loworld_chunk_t* chunk) {
assert(gen != NULL);
assert(chunk != NULL);
const loworld_template_building_param_t param = {
.target = chunk,
.pools = gen->pools,
.seed = loworld_generator_rand_(gen, chunk->pos.x, chunk->pos.y),
};
loworld_template_boss_greedy_scientist_build_chunk(&param);
}
loworld_generator_t* loworld_generator_new(
const loworld_poolset_t* pools, uint64_t seed) {
assert(pools != NULL);
loworld_generator_t* gen = memory_new(sizeof(*gen));
*gen = (typeof(*gen)) {
.pools = pools,
};
loworld_generator_randomize(gen, seed);
return gen;
}
void loworld_generator_delete(loworld_generator_t* gen) {
assert(gen != NULL);
memory_delete(gen);
}
void loworld_generator_randomize(loworld_generator_t* gen, uint64_t seed) {
assert(gen != NULL);
gen->seed = seed;
}
void loworld_generator_generate(
const loworld_generator_t* gen, loworld_chunk_t* chunk) {
assert(gen != NULL);
assert(chunk != NULL);
chunk->biome =
loworld_generator_decide_chunk_biome_(gen, chunk->pos.x, chunk->pos.y);
switch (chunk->biome) {
case LOWORLD_CHUNK_BIOME_METAPHYSICAL_GATE:
loworld_generator_generate_for_metaphysical_gate_(gen, chunk);
break;
case LOWORLD_CHUNK_BIOME_CAVIAS_CAMP:
loworld_generator_generate_for_cavias_camp_(gen, chunk);
break;
case LOWORLD_CHUNK_BIOME_LABORATORY:
loworld_generator_generate_for_laboratory_(gen, chunk);
break;
case LOWORLD_CHUNK_BIOME_BOSS_THEISTS_CHILD:
loworld_generator_generate_for_boss_theists_child_(gen, chunk);
break;
case LOWORLD_CHUNK_BIOME_BOSS_BIG_WARDER:
loworld_generator_generate_for_boss_big_warder_(gen, chunk);
break;
case LOWORLD_CHUNK_BIOME_BOSS_GREEDY_SCIENTIST:
loworld_generator_generate_for_boss_greedy_scientist_(gen, chunk);
break;
}
}
void loworld_generator_pack(
const loworld_generator_t* gen, msgpack_packer* packer) {
assert(gen != NULL);
assert(packer != NULL);
msgpack_pack_uint64(packer, gen->seed);
}
bool loworld_generator_unpack(
loworld_generator_t* gen, const msgpack_object* obj) {
assert(gen != NULL);
assert(obj != NULL);
uint64_t seed;
if (!mpkutil_get_uint64(obj, &seed)) return false;
loworld_generator_randomize(gen, seed);
return true;
}

47
core/loworld/generator.h Normal file
View File

@@ -0,0 +1,47 @@
#pragma once
#include <stdbool.h>
#include <stdint.h>
#include <msgpack.h>
#include "./chunk.h"
#include "./poolset.h"
struct loworld_generator_t;
typedef struct loworld_generator_t loworld_generator_t;
loworld_generator_t*
loworld_generator_new(
const loworld_poolset_t* pools,
uint64_t seed
);
void
loworld_generator_delete(
loworld_generator_t* gen
);
void
loworld_generator_randomize(
loworld_generator_t* gen,
uint64_t seed
);
void
loworld_generator_generate(
const loworld_generator_t* gen,
loworld_chunk_t* chunk
);
void
loworld_generator_pack(
const loworld_generator_t* gen,
msgpack_packer* packer
);
bool
loworld_generator_unpack(
loworld_generator_t* gen,
const msgpack_object* obj
);

38
core/loworld/poolset.c Normal file
View File

@@ -0,0 +1,38 @@
#include "./poolset.h"
#include <assert.h>
#include <stddef.h>
#include <msgpack.h>
#include <msgpack/sbuffer.h>
#include "core/lobullet/pool.h"
#include "core/locharacter/pool.h"
#include "core/loground/pool.h"
#include "./test.h"
loentity_t* loworld_poolset_unpack_entity(
const loworld_poolset_t* pools, const msgpack_object* obj) {
assert(pools != NULL);
assert(obj != NULL);
loentity_t* e;
e = (typeof(e)) loground_pool_unpack_item(pools->ground, obj);
if (e != NULL) return e;
e = (typeof(e)) lobullet_pool_unpack_item(pools->bullet, obj);
if (e != NULL) return e;
e = (typeof(e)) locharacter_pool_unpack_item(pools->character, obj);
if (e != NULL) return e;
return NULL;
}
void loworld_poolset_test_packing(const loworld_poolset_t* pools) {
assert(pools != NULL);
loworld_test_packing(pools);
}

29
core/loworld/poolset.h Normal file
View File

@@ -0,0 +1,29 @@
#pragma once
#include <stddef.h>
#include <msgpack.h>
#include "core/lobullet/pool.h"
#include "core/locharacter/pool.h"
#include "core/loground/pool.h"
typedef struct {
loground_pool_t* ground;
lobullet_pool_t* bullet;
locharacter_pool_t* character;
} loworld_poolset_t;
/* Initialize and Deinitialize each member manually
* because of a dependency issue. */
loentity_t* /* NULLABLE/OWNERSHIP */
loworld_poolset_unpack_entity(
const loworld_poolset_t* pools,
const msgpack_object* obj
);
void
loworld_poolset_test_packing(
const loworld_poolset_t* pools
);

265
core/loworld/store.c Normal file
View File

@@ -0,0 +1,265 @@
#include "./store.h"
#include <assert.h>
#include <errno.h>
#include <inttypes.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <msgpack.h>
#include <msgpack/fbuffer.h>
#include "util/memory/memory.h"
#include "util/mpkutil/file.h"
#include "./chunk.h"
#include "./generator.h"
#include "./poolset.h"
#define LOWORLD_STORE_PATH_MAX_LENGTH 256
#define LOWORLD_STORE_UNPACKER_BUFFER_SIZE (1024*1024)
typedef struct {
loworld_chunk_t data;
bool loaded;
bool used;
uint64_t last_tick;
} loworld_store_chunk_t;
struct loworld_store_t {
char path[LOWORLD_STORE_PATH_MAX_LENGTH+LOWORLD_CHUNK_FILENAME_MAX];
size_t basepath_length;
const loworld_poolset_t* pools;
const loworld_generator_t* generator;
msgpack_unpacker unpacker;
msgpack_unpacked unpacked;
uint64_t tick;
bool error;
size_t chunks_length;
loworld_store_chunk_t chunks[1];
};
static bool loworld_store_find_chunk_index_(
const loworld_store_t* store, size_t* index, int32_t x, int32_t y) {
assert(store != NULL);
assert(index != NULL);
for (size_t i = 0; i < store->chunks_length; ++i) {
if (!store->chunks[i].loaded) continue;
const loworld_chunk_t* chunk = &store->chunks[i].data;
if (chunk->pos.x == x && chunk->pos.y == y) {
*index = i;
return true;
}
}
return false;
}
static bool loworld_store_find_unused_chunk_index_(
const loworld_store_t* store, size_t* index) {
assert(store != NULL);
assert(index != NULL);
for (size_t i = 0; i < store->chunks_length; ++i) {
if (!store->chunks[i].loaded) {
*index = i;
return true;
}
}
bool found = false;
uint64_t oldest = UINT64_MAX;
for (size_t i = 0; i < store->chunks_length; ++i) {
const loworld_store_chunk_t* chunk = &store->chunks[i];
if (!chunk->used && chunk->last_tick <= oldest) {
*index = i;
oldest = chunk->last_tick;
found = true;
}
}
return found;
}
/* Builds filename on the store->path as zero-terminated string. */
static void loworld_store_build_chunk_filename_(
loworld_store_t* store, const loworld_chunk_t* chunk) {
assert(store != NULL);
assert(chunk != NULL);
loworld_chunk_build_filename(chunk,
&store->path[store->basepath_length], LOWORLD_CHUNK_FILENAME_MAX);
}
static bool loworld_store_load_chunk_from_file_(
loworld_store_t* store, loworld_chunk_t* chunk) {
assert(store != NULL);
assert(chunk != NULL);
loworld_store_build_chunk_filename_(store, chunk);
FILE* fp = fopen(store->path, "rb");
if (fp == NULL) return false;
bool success = false;
msgpack_unpacker_reset(&store->unpacker);
if (mpkutil_file_unpack_with_unpacker(
&store->unpacked, fp, &store->unpacker)) {
loworld_chunk_clear(chunk);
success = loworld_chunk_unpack(chunk, &store->unpacked.data, store->pools);
}
fclose(fp);
if (!success) {
fprintf(stderr,
"failed to load chunk (%"PRId32", %"PRId32")\n",
chunk->pos.x, chunk->pos.y);
}
return success;
}
static bool loworld_store_save_chunk_to_file_(
loworld_store_t* store, const loworld_chunk_t* chunk) {
assert(store != NULL);
assert(chunk != NULL);
loworld_store_build_chunk_filename_(store, chunk);
FILE* fp = fopen(store->path, "wb");
if (fp == NULL) return false;
msgpack_packer packer;
msgpack_packer_init(&packer, fp, msgpack_fbuffer_write);
loworld_chunk_pack(chunk, &packer);
const bool success = (ferror(fp) == 0);
fclose(fp);
return success;
}
loworld_store_t* loworld_store_new(
const loworld_poolset_t* pools,
const loworld_generator_t* generator,
size_t chunks_length,
const char* basepath,
size_t basepath_length) {
assert(pools != NULL);
assert(generator != NULL);
assert(chunks_length > 0);
if (basepath_length >= LOWORLD_STORE_PATH_MAX_LENGTH) {
fprintf(stderr, "too long path name\n");
abort();
}
loworld_store_t* store = memory_new(
sizeof(*store) + (chunks_length-1)*sizeof(store->chunks[0]));
*store = (typeof(*store)) {
.basepath_length = basepath_length,
.pools = pools,
.generator = generator,
.chunks_length = chunks_length,
};
strncpy(store->path, basepath, LOWORLD_STORE_PATH_MAX_LENGTH);
if (!msgpack_unpacker_init(
&store->unpacker, LOWORLD_STORE_UNPACKER_BUFFER_SIZE)) {
fprintf(stderr, "failed to initialize unpacker\n");
abort();
}
msgpack_unpacked_init(&store->unpacked);
for (size_t i = 0; i < store->chunks_length; ++i) {
loworld_store_chunk_t* chunk = &store->chunks[i];
*chunk = (typeof(*chunk)) {0};
loworld_chunk_initialize(&chunk->data);
}
return store;
}
void loworld_store_delete(loworld_store_t* store) {
if (store == NULL) return;
msgpack_unpacker_destroy(&store->unpacker);
msgpack_unpacked_destroy(&store->unpacked);
for (size_t i = 0; i < store->chunks_length; ++i) {
loworld_chunk_deinitialize(&store->chunks[i].data);
}
memory_delete(store);
}
loworld_chunk_t* loworld_store_load_chunk(
loworld_store_t* store, int32_t chunk_x, int32_t chunk_y) {
assert(store != NULL);
size_t index;
if (!loworld_store_find_chunk_index_(store, &index, chunk_x, chunk_y)) {
if (!loworld_store_find_unused_chunk_index_(store, &index)) {
fprintf(stderr, "world store chunk overflow\n");
abort();
}
}
loworld_store_chunk_t* chunk = &store->chunks[index];
if (chunk->loaded &&
(chunk->data.pos.x != chunk_x || chunk->data.pos.y != chunk_y)) {
assert(!chunk->used);
store->error = !loworld_store_save_chunk_to_file_(store, &chunk->data);
chunk->loaded = false;
}
if (!chunk->loaded) {
chunk->data.pos.x = chunk_x;
chunk->data.pos.y = chunk_y;
if (!loworld_store_load_chunk_from_file_(store, &chunk->data)) {
loworld_chunk_clear(&chunk->data);
loworld_generator_generate(store->generator, &chunk->data);
}
chunk->loaded = true;
}
chunk->used = true;
chunk->last_tick = store->tick++;
return &chunk->data;
}
void loworld_store_unload_chunk(
loworld_store_t* store, loworld_chunk_t* chunk) {
assert(store != NULL);
assert(chunk != NULL);
loworld_store_chunk_t* c = (typeof(c)) chunk;
assert(store->chunks <= c && c < store->chunks+store->chunks_length);
c->used = false;
}
void loworld_store_flush(loworld_store_t* store) {
assert(store != NULL);
for (size_t i = 0; i < store->chunks_length; ++i) {
loworld_store_chunk_t* chunk = &store->chunks[i];
if (!chunk->loaded) continue;
store->error = !loworld_store_save_chunk_to_file_(store, &chunk->data);
}
}
bool loworld_store_is_error_happened(const loworld_store_t* store) {
assert(store != NULL);
return store->error;
}

52
core/loworld/store.h Normal file
View File

@@ -0,0 +1,52 @@
#pragma once
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include "./chunk.h"
#include "./generator.h"
#include "./poolset.h"
struct loworld_store_t;
typedef struct loworld_store_t loworld_store_t;
/* TODO(catfoot): make it possible to specify a path to chunk dir */
loworld_store_t* /* OWNERSHIP */
loworld_store_new(
const loworld_poolset_t* pools,
const loworld_generator_t* gen,
size_t chunks_length,
const char* basepath, /* must be terminated with slash */
size_t basepath_length
);
void
loworld_store_delete(
loworld_store_t* store /* OWNERSHIP */
);
loworld_chunk_t* /* NULLABLE */
loworld_store_load_chunk(
loworld_store_t* store,
int32_t chunk_x,
int32_t chunk_y
);
void
loworld_store_unload_chunk(
loworld_store_t* store,
loworld_chunk_t* chunk
);
/* If there is an instance of loworld_view_t, this function may flush broken
chunks. So use loworld_view_flush_store function insteadly. */
void
loworld_store_flush(
loworld_store_t* store
);
bool
loworld_store_is_error_happened(
const loworld_store_t* store
);

287
core/loworld/template.c Normal file
View File

@@ -0,0 +1,287 @@
#include "./template.h"
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include "util/chaos/xorshift.h"
#include "util/math/algorithm.h"
#include "util/math/vector.h"
#include "core/locharacter/base.h"
#include "core/locharacter/big_warder.h"
#include "core/locharacter/cavia.h"
#include "core/locharacter/encephalon.h"
#include "core/locharacter/scientist.h"
#include "core/locharacter/greedy_scientist.h"
#include "core/locharacter/theists_child.h"
#include "core/locharacter/warder.h"
#include "core/locommon/position.h"
#include "core/loentity/entity.h"
#include "core/loground/base.h"
#include "core/loground/island.h"
#include "./chunk.h"
#include "./poolset.h"
static loentity_id_t loworld_template_add_ground_island_(
const loworld_template_building_param_t* param,
const vec2_t* pos,
const vec2_t* sz) {
assert(loworld_template_building_param_valid(param));
assert(vec2_valid(pos));
assert(vec2_valid(sz));
assert(sz->x >= 0 && sz->y >= 0);
const locommon_position_t p = locommon_position(
param->target->pos.x, param->target->pos.y, *pos);
loground_base_t* island = loground_pool_create(param->pools->ground);
loground_island_build(island, &p, sz);
loworld_chunk_add_entity(param->target, &island->super.super);
return island->super.super.id;
}
static loentity_id_t loworld_template_add_character_random_enemy_(
const loworld_template_building_param_t* param,
uint64_t seed,
loentity_id_t ground,
float pos) {
assert(param != NULL);
locharacter_base_t* base = locharacter_pool_create(param->pools->character);
bool built = false;
const uint64_t type = (seed = chaos_xorshift(seed))%3;
switch (type) {
case 1:
locharacter_warder_build(base, &(locharacter_warder_param_t) {
.ground = ground,
.pos = pos,
});
built = true;
break;
case 2:
locharacter_scientist_build(base, &(locharacter_scientist_param_t) {
.ground = ground,
.pos = pos,
.direction = (seed = chaos_xorshift(seed))%2? 1: -1,
});
built = true;
break;
}
if (!built) {
locharacter_cavia_build(base, &(locharacter_cavia_param_t) {
.ground = ground,
.pos = pos,
.direction = (seed = chaos_xorshift(seed))%2? 1: -1,
});
}
loworld_chunk_add_entity(param->target, &base->super.super);
return base->super.super.id;
}
bool loworld_template_building_param_valid(
const loworld_template_building_param_t* param) {
return
param != NULL &&
param->target != NULL &&
param->pools != NULL;
}
void loworld_template_metaphysical_gate_build_chunk(
const loworld_template_building_param_t* param) {
assert(loworld_template_building_param_valid(param));
loworld_template_add_ground_island_(
param, &vec2(.5f, .2f), &vec2(.5f, .1f));
const loentity_id_t ground = loworld_template_add_ground_island_(
param, &vec2(.5f, .45f), &vec2(.2f, .02f));
locharacter_base_t* encephalon =
locharacter_pool_create(param->pools->character);
locharacter_encephalon_build(encephalon, ground);
loworld_chunk_add_entity(param->target, &encephalon->super.super);
}
void loworld_template_open_space_build_chunk(
const loworld_template_building_param_t* param) {
assert(loworld_template_building_param_valid(param));
uint64_t s = param->seed;
const size_t enemy_count = (s = chaos_xorshift(s))%3+1;
const loentity_id_t ground = loworld_template_add_ground_island_(
param, &vec2(.5f, .2f), &vec2(.47f, .01f));
for (size_t i = 0; i < enemy_count; ++i) {
loworld_template_add_character_random_enemy_(
param, (s = chaos_xorshift(s)), ground, (2.0f/enemy_count*i - 1)*.75f);
}
}
void loworld_template_broken_open_space_build_chunk(
const loworld_template_building_param_t* param) {
assert(loworld_template_building_param_valid(param));
uint64_t s = param->seed;
const loentity_id_t floor1 = loworld_template_add_ground_island_(
param, &vec2(.2f, .2f), &vec2(.18f, .01f));
if ((s = chaos_xorshift(s))%2) {
loworld_template_add_character_random_enemy_(
param, (s = chaos_xorshift(s)), floor1, .5f);
}
const loentity_id_t floor2 = loworld_template_add_ground_island_(
param, &vec2(.8f, .2f), &vec2(.18f, .01f));
if ((s = chaos_xorshift(s))%2) {
loworld_template_add_character_random_enemy_(
param, (s = chaos_xorshift(s)), floor2, .5f);
}
loworld_template_add_ground_island_(
param, &vec2(.5f, .05f), &vec2(.15f, .01f));
}
void loworld_template_passage_build_chunk(
const loworld_template_building_param_t* param) {
assert(loworld_template_building_param_valid(param));
uint64_t s = param->seed;
const loentity_id_t floor = loworld_template_add_ground_island_(
param, &vec2(.5f, .25f), &vec2(.5f, .01f));
if ((s = chaos_xorshift(s))%2) {
loworld_template_add_character_random_enemy_(
param, (s = chaos_xorshift(s)), floor, .5f);
}
const loentity_id_t ceiling = loworld_template_add_ground_island_(
param, &vec2(.55f, .4f), &vec2(.3f, .007f));
if ((s = chaos_xorshift(s))%2) {
loworld_template_add_character_random_enemy_(
param, (s = chaos_xorshift(s)), ceiling, .2f);
}
}
void loworld_template_broken_passage_build_chunk(
const loworld_template_building_param_t* param) {
assert(loworld_template_building_param_valid(param));
uint64_t s = param->seed;
const loentity_id_t floor1 = loworld_template_add_ground_island_(
param, &vec2(.15f, .25f), &vec2(.15f, .01f));
if ((s = chaos_xorshift(s))%2) {
loworld_template_add_character_random_enemy_(
param, (s = chaos_xorshift(s)), floor1, .5f);
}
const loentity_id_t floor2 = loworld_template_add_ground_island_(
param, &vec2(.45f, .25f), &vec2(.1f, .01f));
if ((s = chaos_xorshift(s))%2) {
loworld_template_add_character_random_enemy_(
param, (s = chaos_xorshift(s)), floor2, .5f);
}
const loentity_id_t floor3 = loworld_template_add_ground_island_(
param, &vec2(.85f, .25f), &vec2(.15f, .01f));
if ((s = chaos_xorshift(s))%2) {
loworld_template_add_character_random_enemy_(
param, (s = chaos_xorshift(s)), floor3, .5f);
}
const uint64_t layout = (s = chaos_xorshift(s))%3;
if (layout == 0 || layout == 1) {
const loentity_id_t ceiling = loworld_template_add_ground_island_(
param, &vec2(.2f, .4f), &vec2(.15f, .007f));
if ((s = chaos_xorshift(s))%2) {
loworld_template_add_character_random_enemy_(
param, (s = chaos_xorshift(s)), ceiling, .5f);
}
}
if (layout == 0 || layout == 2) {
const loentity_id_t ceiling = loworld_template_add_ground_island_(
param, &vec2(.7f, .38f), &vec2(.12f, .007f));
if ((s = chaos_xorshift(s))%2) {
loworld_template_add_character_random_enemy_(
param, (s = chaos_xorshift(s)), ceiling, .5f);
}
}
}
void loworld_template_stairs_build_chunk(
const loworld_template_building_param_t* param) {
assert(loworld_template_building_param_valid(param));
uint64_t s = param->seed;
const loentity_id_t floor1 = loworld_template_add_ground_island_(
param, &vec2(.5f, .3f), &vec2(.5f, .015f));
if ((s = chaos_xorshift(s))%2) {
loworld_template_add_character_random_enemy_(
param, (s = chaos_xorshift(s)), floor1, .5f);
}
bool layout = (s = chaos_xorshift(s))%2;
const loentity_id_t floor2 = loworld_template_add_ground_island_(
param, &vec2(layout? .3f: .6f, .5f), &vec2(.2f, .015f));
if ((s = chaos_xorshift(s))%2) {
loworld_template_add_character_random_enemy_(
param, (s = chaos_xorshift(s)), floor2, .5f);
}
layout = !layout;
const loentity_id_t floor3 = loworld_template_add_ground_island_(
param, &vec2(layout? .2f: .8f, .7f), &vec2(.18f, .007f));
if ((s = chaos_xorshift(s))%2) {
loworld_template_add_character_random_enemy_(
param, (s = chaos_xorshift(s)), floor3, .5f);
}
const loentity_id_t floor4 = loworld_template_add_ground_island_(
param, &vec2(.5f, .9f), &vec2(.32f, .007f));
if ((s = chaos_xorshift(s))%2) {
loworld_template_add_character_random_enemy_(
param, (s = chaos_xorshift(s)), floor4, .5f);
}
}
void loworld_template_boss_theists_child_build_chunk(
const loworld_template_building_param_t* param) {
assert(loworld_template_building_param_valid(param));
const loentity_id_t ground = loworld_template_add_ground_island_(
param, &vec2(.5f, .1f), &vec2(.5f, .05f));
locharacter_base_t* boss = locharacter_pool_create(param->pools->character);
locharacter_theists_child_build(boss, ground);
loworld_chunk_add_entity(param->target, &boss->super.super);
}
void loworld_template_boss_big_warder_build_chunk(
const loworld_template_building_param_t* param) {
assert(loworld_template_building_param_valid(param));
const loentity_id_t ground = loworld_template_add_ground_island_(
param, &vec2(.5f, .1f), &vec2(.5f, .05f));
locharacter_base_t* boss = locharacter_pool_create(param->pools->character);
locharacter_big_warder_build(boss, ground);
loworld_chunk_add_entity(param->target, &boss->super.super);
}
void loworld_template_boss_greedy_scientist_build_chunk(
const loworld_template_building_param_t* param) {
assert(loworld_template_building_param_valid(param));
const loentity_id_t ground = loworld_template_add_ground_island_(
param, &vec2(.5f, .1f), &vec2(.5f, .05f));
locharacter_base_t* boss = locharacter_pool_create(param->pools->character);
locharacter_greedy_scientist_build(boss, ground);
loworld_chunk_add_entity(param->target, &boss->super.super);
}

138
core/loworld/template.h Normal file
View File

@@ -0,0 +1,138 @@
#pragma once
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include "./chunk.h"
#include "./poolset.h"
#define LOWORLD_TEMPLATE_MAX_CHARACTERS_PER_CHUNK 5
#define LOWORLD_TEMPLATE_MAX_GROUNDS_PER_CHUNK 5
typedef struct {
loworld_chunk_t* target;
const loworld_poolset_t* pools;
uint64_t seed;
} loworld_template_building_param_t;
bool
loworld_template_building_param_valid(
const loworld_template_building_param_t* param
);
/* [metaphysical gate]
* 2 grounds (=) and the one encephalon statue (E)
*
* E
* ======
*
* ==================
*/
void
loworld_template_metaphysical_gate_build_chunk(
const loworld_template_building_param_t* param
);
/* [open space]
* 1 ground (=) and 0~3 enemies (E)
*
*
* E E E
* ==================
*/
void
loworld_template_open_space_build_chunk(
const loworld_template_building_param_t* param
);
/* [broken open space]
* 1 ground (=) and 0~2 enemies (E)
*
*
* E E
* ======== ========
*
* ======
*/
void
loworld_template_broken_open_space_build_chunk(
const loworld_template_building_param_t* param
);
/* [passage]
* 1 ground (=), ceiling (-), and 0~2 enemies (E)
*
* E
* ----------
* E
* =================
*/
void
loworld_template_passage_build_chunk(
const loworld_template_building_param_t* param
);
/* [broken passage]
* 3 grounds (=), 1 or 2 ceilings (-), and 0~5 enemies (E)
*
* E E
* ---- ----
* E E E
* ==== ==== =====
*/
void
loworld_template_broken_passage_build_chunk(
const loworld_template_building_param_t* param
);
/* [stairs]
* 4 grounds (=), and 0~4 enemies (E)
*
* E
* =========
* E
* ======
* E
* =========
* E
* ==================
*/
void
loworld_template_stairs_build_chunk(
const loworld_template_building_param_t* param
);
/* [BOSS: thiest's child]
* 1 ground (=), and The Theist's Child (E)
*
* E
* =================
*/
void
loworld_template_boss_theists_child_build_chunk(
const loworld_template_building_param_t* param
);
/* [BOSS: big warder]
* 1 ground (=), and The Big Warder (E)
*
* E
* =================
*/
void
loworld_template_boss_big_warder_build_chunk(
const loworld_template_building_param_t* param
);
/* [BOSS: greedy scientist]
* 1 ground (=), and The Greedy Scientist (E)
*
* E
* =================
*/
void
loworld_template_boss_greedy_scientist_build_chunk(
const loworld_template_building_param_t* param
);

146
core/loworld/test.h Normal file
View File

@@ -0,0 +1,146 @@
#pragma once
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <msgpack.h>
#include <msgpack/sbuffer.h>
#include "core/lobullet/base.h"
#include "core/lobullet/bomb.h"
#include "core/lobullet/linear.h"
#include "core/locharacter/base.h"
#include "core/locharacter/big_warder.h"
#include "core/locharacter/cavia.h"
#include "core/locharacter/encephalon.h"
#include "core/locharacter/greedy_scientist.h"
#include "core/locharacter/theists_child.h"
#include "core/locharacter/scientist.h"
#include "core/locharacter/warder.h"
#include "core/locharacter/pool.h"
#include "core/loentity/entity.h"
#include "core/loground/base.h"
#include "core/loground/island.h"
#include "core/loground/pool.h"
#include "./poolset.h"
# define pack_and_unpack_(type, name) do { \
msgpack_sbuffer_clear(buf); \
loentity_pack(&base->super.super, &packer); \
loentity_delete(&base->super.super); \
\
size_t offset = 0; \
const msgpack_unpack_return r = \
msgpack_unpack_next(upk, buf->data, buf->size, &offset); \
if (r != MSGPACK_UNPACK_SUCCESS) { \
fprintf(stderr, #type"_"#name": invalid msgpack format\n"); \
abort(); \
} \
if (!type##_base_unpack(base, &upk->data)) { \
fprintf(stderr, #type"_"#name": failed to unpack\n"); \
abort(); \
} \
loentity_delete(&base->super.super); \
printf(#type"_"#name": pack test passed\n"); \
} while (0)
static inline void loworld_test_packing_grounds(
loground_base_t* base, msgpack_sbuffer* buf, msgpack_unpacked* upk) {
assert(base != NULL);
assert(buf != NULL);
assert(upk != NULL);
msgpack_packer packer;
msgpack_packer_init(&packer, buf, msgpack_sbuffer_write);
loground_island_build(
base, &locommon_position(0, 0, vec2(0, 0)), &vec2(1, 1));
pack_and_unpack_(loground, island);
}
static inline void loworld_test_packing_bullets(
lobullet_base_t* base, msgpack_sbuffer* buf, msgpack_unpacked* upk) {
assert(base != NULL);
assert(buf != NULL);
assert(upk != NULL);
msgpack_packer packer;
msgpack_packer_init(&packer, buf, msgpack_sbuffer_write);
lobullet_bomb_square_build(
base, &((lobullet_bomb_param_t) { .step = 1, }));
pack_and_unpack_(lobullet, bomb);
lobullet_bomb_triangle_build(
base, &((lobullet_bomb_param_t) { .step = 1, }));
pack_and_unpack_(lobullet, bomb);
lobullet_linear_light_build(
base, &((lobullet_linear_param_t) { .duration = 1, }));
pack_and_unpack_(lobullet, linear);
lobullet_linear_triangle_build(
base, &((lobullet_linear_param_t) { .duration = 1, }));
pack_and_unpack_(lobullet, linear);
}
static inline void loworld_test_packing_characters(
locharacter_base_t* base, msgpack_sbuffer* buf, msgpack_unpacked* upk) {
assert(base != NULL);
assert(buf != NULL);
assert(upk != NULL);
msgpack_packer packer;
msgpack_packer_init(&packer, buf, msgpack_sbuffer_write);
locharacter_big_warder_build(base, 0);
pack_and_unpack_(locharacter, big_warder);
locharacter_cavia_build(
base, &((locharacter_cavia_param_t) { .direction = 1, }));
pack_and_unpack_(locharacter, cavia);
locharacter_encephalon_build(base, 0);
pack_and_unpack_(locharacter, encephalon);
locharacter_theists_child_build(base, 0);
pack_and_unpack_(locharacter, theists_child);
locharacter_greedy_scientist_build(base, 0);
pack_and_unpack_(locharacter, greedy_scientist);
locharacter_scientist_build(base, &((locharacter_scientist_param_t) {0}));
pack_and_unpack_(locharacter, scientist);
locharacter_warder_build(base, &((locharacter_warder_param_t) {0}));
pack_and_unpack_(locharacter, warder);
}
# undef pack_and_unpack_
static inline void loworld_test_packing(const loworld_poolset_t* pools) {
assert(pools != NULL);
msgpack_sbuffer buf;
msgpack_sbuffer_init(&buf);
msgpack_unpacked upk;
msgpack_unpacked_init(&upk);
loground_base_t* ground = loground_pool_create(pools->ground);
loworld_test_packing_grounds(ground, &buf, &upk);
loentity_delete(&ground->super.super);
lobullet_base_t* bullet = lobullet_pool_create(pools->bullet);
loworld_test_packing_bullets(bullet, &buf, &upk);
loentity_delete(&bullet->super.super);
locharacter_base_t* chara = locharacter_pool_create(pools->character);
loworld_test_packing_characters(chara, &buf, &upk);
loentity_delete(&chara->super.super);
msgpack_unpacked_destroy(&upk);
msgpack_sbuffer_destroy(&buf);
}

171
core/loworld/view.c Normal file
View File

@@ -0,0 +1,171 @@
#include "./view.h"
#include <assert.h>
#include <stddef.h>
#include "util/math/vector.h"
#include "util/memory/memory.h"
#include "core/locommon/position.h"
#include "core/loentity/store.h"
#include "./chunk.h"
#include "./store.h"
#define LOWORLD_VIEW_CHUNK_LOAD_RANGE 2
struct loworld_view_t {
loworld_store_t* world;
loentity_store_t* entities;
locommon_position_t looking;
loworld_chunk_t* chunks
[LOWORLD_VIEW_CHUNK_LOAD_RANGE*2+1][LOWORLD_VIEW_CHUNK_LOAD_RANGE*2+1];
};
static void loworld_view_stage_chunk_(
loworld_view_t* view, loworld_chunk_t* chunk) {
assert(view != NULL);
assert(chunk != NULL);
const size_t len = container_array_get_length(chunk->entities);
for (size_t i = 0; i < len; ++i) {
/* moving ownership from the chunk to the store */
loentity_store_add(view->entities, chunk->entities[i]);
}
container_array_resize(chunk->entities, 0);
}
static void loworld_view_unstage_chunk_(
loworld_view_t* view, loworld_chunk_t* chunk) {
assert(view != NULL);
assert(chunk != NULL);
loentity_store_iterator_t itr = {0};
while (loentity_store_iterate_next(view->entities, &itr)) {
if (itr.entity->dont_save ||
itr.entity->pos.chunk.x != chunk->pos.x ||
itr.entity->pos.chunk.y != chunk->pos.y) {
continue;
}
const size_t index = container_array_get_length(chunk->entities);
container_array_insert(chunk->entities, index);
/* moving ownership from the store to the chunk */
chunk->entities[index] = loentity_store_remove(view->entities, &itr);
}
}
static void loworld_view_load_all_chunks_(loworld_view_t* view) {
assert(view != NULL);
/* Unload all chunks before calling this function. */
static const int32_t r = LOWORLD_VIEW_CHUNK_LOAD_RANGE;
for (int32_t x = -r; x <= r; ++x) {
for (int32_t y = -r; y <= r; ++y) {
loworld_chunk_t* chunk = loworld_store_load_chunk(
view->world, view->looking.chunk.x + x, view->looking.chunk.y + y);
view->chunks[y+r][x+r] = chunk;
loworld_view_stage_chunk_(view, chunk);
}
}
}
static void loworld_view_unload_all_chunks_(loworld_view_t* view) {
assert(view != NULL);
static const int32_t r = LOWORLD_VIEW_CHUNK_LOAD_RANGE;
for (int32_t x = -r; x <= r; ++x) {
for (int32_t y = -r; y <= r; ++y) {
loworld_chunk_t* chunk = view->chunks[y+r][x+r];
if (chunk == NULL) continue;
loworld_view_unstage_chunk_(view, chunk);
loworld_store_unload_chunk(view->world, chunk);
}
}
}
loworld_view_t* loworld_view_new(
loworld_store_t* world,
loentity_store_t* entities,
const locommon_position_t* looking) {
assert(world != NULL);
assert(entities != NULL);
assert(locommon_position_valid(looking));
loworld_view_t* view = memory_new(sizeof(*view));
*view = (typeof(*view)) {
.world = world,
.entities = entities,
.looking = *looking,
};
loworld_view_load_all_chunks_(view);
return view;
}
void loworld_view_delete(loworld_view_t* view) {
if (view == NULL) return;
loworld_view_unload_all_chunks_(view);
memory_delete(view);
}
void loworld_view_update(loworld_view_t* view) {
assert(view != NULL);
loentity_store_iterator_t itr = {0};
while (loentity_store_iterate_next(view->entities, &itr)) {
if (!loentity_update(itr.entity)) {
loentity_delete(loentity_store_remove(view->entities, &itr));
}
}
}
void loworld_view_draw(loworld_view_t* view) {
assert(view != NULL);
loentity_store_iterator_t itr = {0};
while (loentity_store_iterate_next(view->entities, &itr)) {
loentity_draw(itr.entity, &view->looking);
}
}
void loworld_view_look(loworld_view_t* view, const locommon_position_t* pos) {
assert(view != NULL);
assert(locommon_position_valid(pos));
const bool chunk_moved =
view->looking.chunk.x != pos->chunk.x ||
view->looking.chunk.y != pos->chunk.y;
view->looking = *pos;
if (chunk_moved) {
loworld_view_unload_all_chunks_(view);
loworld_view_load_all_chunks_(view);
}
}
void loworld_view_flush_store(loworld_view_t* view) {
assert(view != NULL);
loworld_view_unload_all_chunks_(view);
loworld_store_flush(view->world);
loworld_view_load_all_chunks_(view);
}
const loworld_chunk_t* loworld_view_get_looking_chunk(
const loworld_view_t* view) {
assert(view != NULL);
static const int32_t r = LOWORLD_VIEW_CHUNK_LOAD_RANGE;
return view->chunks[r][r];
}

49
core/loworld/view.h Normal file
View File

@@ -0,0 +1,49 @@
#pragma once
#include "core/locommon/position.h"
#include "core/loentity/store.h"
#include "./chunk.h"
#include "./store.h"
struct loworld_view_t;
typedef struct loworld_view_t loworld_view_t;
loworld_view_t* /* OWNERSHIP */
loworld_view_new(
loworld_store_t* world,
loentity_store_t* entities,
const locommon_position_t* looking
);
void
loworld_view_delete(
loworld_view_t* view /* OWNERSHIP */
);
void
loworld_view_update(
loworld_view_t* view
);
void
loworld_view_draw(
loworld_view_t* view
);
void
loworld_view_look(
loworld_view_t* view,
const locommon_position_t* pos
);
/* Flushes all chunks safely including currently staged ones. */
void
loworld_view_flush_store(
loworld_view_t* view
);
const loworld_chunk_t* /* ALIVES UNTIL NEXT OPERATION */
loworld_view_get_looking_chunk(
const loworld_view_t* view
);