mirror of
https://github.com/zhenyan121/Cubed.git
synced 2026-08-08 09:47:01 +08:00
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:
@@ -50,6 +50,8 @@ public:
|
||||
void generate_cave();
|
||||
void generate_river();
|
||||
|
||||
void spawn_creature();
|
||||
|
||||
private:
|
||||
static inline std::atomic<bool> is_init{false};
|
||||
static inline unsigned m_generator_seed{0};
|
||||
|
||||
20
include/Cubed/gameplay/creatures/spawn.hpp
Normal file
20
include/Cubed/gameplay/creatures/spawn.hpp
Normal file
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
#include "Cubed/gameplay/biome.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <span>
|
||||
#include <string_view>
|
||||
namespace Cubed {
|
||||
struct SpawnConfig {
|
||||
std::string_view name; // factory key, e.g. "cubed:pig"
|
||||
std::span<const BiomeType> biomes; // allowed biomes
|
||||
float probability = 0.0f; // per chunk spawn probability
|
||||
unsigned max_spawn_count = 0; // per chunk_max_spawn_sum
|
||||
};
|
||||
|
||||
namespace SpawnDefaults {
|
||||
constexpr std::array<BiomeType, 2> PIG_BIOMES{BiomeType::PLAIN,
|
||||
BiomeType::FOREST};
|
||||
constexpr SpawnConfig PIG{"cubed:pig", PIG_BIOMES, 0.02f, 3};
|
||||
} // namespace SpawnDefaults
|
||||
} // namespace Cubed
|
||||
@@ -15,7 +15,7 @@ public:
|
||||
void init();
|
||||
void update();
|
||||
// not thread safe
|
||||
void add_entity(std::string_view name, const glm::vec3& pos);
|
||||
void add_entity(std::string_view name, const glm::vec3& world_pos);
|
||||
void destory(EntityID id);
|
||||
void handle_player_login(std::shared_ptr<Session> session);
|
||||
|
||||
|
||||
@@ -95,6 +95,7 @@ public:
|
||||
std::vector<std::shared_ptr<Session>> get_all_session() const;
|
||||
|
||||
uint32_t get_chunk_ref_count(const glm::vec3& pos) const;
|
||||
ServerEntityManager& entity_manager();
|
||||
|
||||
int get_block(const glm::ivec3& block_pos) const override;
|
||||
bool is_solid(const glm::ivec3& block_pos) const override;
|
||||
|
||||
@@ -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; }
|
||||
|
||||
@@ -141,6 +141,7 @@ void ServerChunk::gen_phase_five() {
|
||||
m_generator->generate_cave();
|
||||
|
||||
m_generator->generate_vegetation();
|
||||
m_generator->spawn_creature();
|
||||
m_generator = nullptr;
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user