feat(gameplay): spawn creatures during chunk generation

Add creature spawning to chunk generation with a configurable
SpawnConfig, including a default pig spawn. Spawning occurs in
the final generation phase and registers entities through the
server world's entity manager.

Also destroy entities when their chunk is unloaded to prevent
orphaned entities and expose the entity manager from ServerWorld.
This commit is contained in:
2026-08-05 16:56:02 +08:00
parent 322fb2fea0
commit 8ff789c576
8 changed files with 62 additions and 4 deletions

View File

@@ -9,6 +9,7 @@
#include "Cubed/gameplay/builders/river_builder.hpp"
#include "Cubed/gameplay/builders/snowy_plain_builder.hpp"
#include "Cubed/gameplay/cave_path.hpp"
#include "Cubed/gameplay/creatures/spawn.hpp"
#include "Cubed/gameplay/river.path.hpp"
#include "Cubed/gameplay/server_chunk.hpp"
#include "Cubed/gameplay/server_world.hpp"
@@ -17,6 +18,8 @@
#include "Cubed/tools/cubed_hash.hpp"
#include "Cubed/tools/math_tools.hpp"
#include "Cubed/tools/perlin_noise.hpp"
#include <algorithm>
namespace Cubed {
namespace {
@@ -805,6 +808,35 @@ void ChunkGenerator::generate_river() {
}
}
void ChunkGenerator::spawn_creature() {
auto biome = m_chunk.biome();
const auto& blocks = m_chunk.blocks();
const auto& heightmap = m_chunk.heightmap();
const auto& chunk_pos = m_chunk.chunk_pos();
if (std::ranges::contains(SpawnDefaults::PIG.biomes, biome)) {
if (m_random.random_bool(SpawnDefaults::PIG.probability)) {
int want =
m_random.random_int(0, SpawnDefaults::PIG.max_spawn_count);
for (int i = 0; i < want; ++i) {
int x = m_random.random_int(0, CHUNK_SIZE - 1);
int z = m_random.random_int(0, CHUNK_SIZE - 1);
int y = static_cast<int>(heightmap[x][z]);
glm::vec3 pos(x, y + 1, z);
auto type = blocks[Chunk::index(pos)];
if (type != 0) {
continue;
}
auto [world_x, world_y, world_z] = Chunk::block_to_world(
x, y + 1, z, chunk_pos.x, chunk_pos.z);
m_chunk.world().entity_manager().add_entity(
SpawnDefaults::PIG.name,
glm::vec3{world_x, world_y, world_z});
}
}
}
}
ServerChunk& ChunkGenerator::chunk() { return m_chunk; }
Random& ChunkGenerator::random() { return m_random; }

View File

@@ -141,6 +141,7 @@ void ServerChunk::gen_phase_five() {
m_generator->generate_cave();
m_generator->generate_vegetation();
m_generator->spawn_creature();
m_generator = nullptr;
}

View File

@@ -37,6 +37,8 @@ void ServerEntityManager::update() {
for (auto e : view) {
const auto& c = view.get<BaseServerCreature>(e);
if (!m_world.get_chunk_ref_count(c.transform.position.value)) {
const auto& entity = m_registry.get<Entity>(e);
destory(entity.id);
continue;
}
update_ai(e);
@@ -102,9 +104,9 @@ void ServerEntityManager::handle_task() {
}
void ServerEntityManager::add_entity(std::string_view name,
const glm::vec3& pos) {
const glm::vec3& world_pos) {
m_tasks.emplace(Command::CREATE,
EntityCreateElement{std::string(name), pos});
EntityCreateElement{std::string(name), world_pos});
}
void ServerEntityManager::destory(EntityID id) {

View File

@@ -1114,5 +1114,5 @@ BlockType ServerWorld::get_block_tpye(const glm::ivec3& block_pos) const {
}
int ServerWorld::get_per_tick_time() const { return m_per_tick_time; }
ServerEntityManager& ServerWorld::entity_manager() { return m_entity_manager; }
} // namespace Cubed