#ifndef SHARED_WORLD_HH_ #define SHARED_WORLD_HH_ #include #include #include #include #include #include #include #include #include #include #include #include "shared/math.hh" #include "shared/net/proto.hh" #include "shared/shared.hh" namespace shared { namespace world { class block { public: enum class type : std::uint8_t { air, // zero means air which means don't render dirt, // atlas should start from here grass, sand, sandstone, stone, cobblestone, wood, leaves, water, shrub, snow, cactus, dead_shrub, snowy_wood, snowy_leaves, snowy_shrub, }; enum class visibility { solid, partial, translucent, invisible }; public: type type; public: static constexpr enum block::visibility get_visibility(const enum block::type type) noexcept { switch (type) { case type::air: return visibility::invisible; case type::shrub: case type::dead_shrub: case type::leaves: case type::snowy_leaves: case type::snowy_shrub: return visibility::partial; case type::water: return visibility::translucent; default: break; } return visibility::solid; } static constexpr bool is_tangible(const enum block::type type) noexcept { switch (type) { case type::air: case type::shrub: case type::dead_shrub: case type::snowy_shrub: return false; default: break; } return true; } static constexpr bool is_collidable(const enum block::type type) noexcept { switch (type) { case type::air: case type::shrub: case type::dead_shrub: case type::snowy_shrub: case type::water: return false; default: break; } return true; } static constexpr bool is_liquid(const enum block::type type) noexcept { switch (type) { case type::water: return true; default: break; } return false; } static constexpr bool is_replaceable(const enum block::type type) noexcept { switch (type) { case type::water: case type::air: return true; default: break; } return false; } static constexpr bool is_removable(const enum block::type type) noexcept { switch (type) { case type::water: case type::air: return false; default: break; } return true; } static constexpr float get_friction(const enum block::type type) noexcept { switch (type) { case type::water: return 20.0f; case type::air: case type::shrub: case type::dead_shrub: case type::snowy_shrub: return 0.1f; default: break; } return 10.0f; } public: block(const enum block::type t = type::air) noexcept : type(t) {} operator enum block::type() const noexcept { return this->type; } }; class chunk { public: static constexpr int WIDTH = 16; static constexpr int HEIGHT = 256; static constexpr int VOLUME = WIDTH * WIDTH * HEIGHT; // Stuff for unordered_map. static std::size_t hash(const shared::math::coords& c) noexcept { std::size_t seed = 0; boost::hash_combine(seed, boost::hash_value(c.x)); boost::hash_combine(seed, boost::hash_value(c.z)); return seed; } static std::size_t equal(const shared::math::coords& a, const shared::math::coords& b) noexcept { return a == b; } using map = std::unordered_map; protected: shared::math::coords pos; std::array blocks; // Use get_block for 3d index access. public: using block_array = decltype(chunk::blocks); enum class biome { desert, islands, ocean, plains, forest, tundra, alpine }; static bool is_outside_chunk(const glm::ivec3& v) noexcept { return (std::min(v.x, v.z) < 0) || (std::max(v.x, v.z) >= WIDTH) || (std::clamp(v.y, 0, HEIGHT - 1) != v.y); } static shared::math::coords get_normalised_chunk(const shared::math::coords& coords, const int x, const int z) noexcept { return coords + shared::math::coords{(x >= WIDTH) - (x < 0), (z >= WIDTH) - (z < 0)}; } static std::pair get_normalised_coords(const int x, const int z) noexcept { return {x + WIDTH * ((x < 0) - (x >= WIDTH)), z + WIDTH * ((z < 0) - (z >= WIDTH))}; } static glm::ivec3 get_normalised_coords(const glm::ivec3& c) noexcept { const auto [x, z] = get_normalised_coords(c.x, c.z); return {x, c.y, z}; } static block_array make_blocks_from_chunk(const proto::chunk& chunk) noexcept; public: static unsigned long get_3d_index(const glm::ivec3& v) noexcept { #ifndef NDEBUG if (is_outside_chunk(v)) { throw std::range_error("bad chunk block coordinate access"); } #endif return static_cast(v.x + (WIDTH * (v.y + (HEIGHT * v.z)))); } public: // If a third non-std::nullopt argument is provided, we use those blocks // instead of what we might have otherwise generated. chunk(const std::uint64_t& seed, const shared::math::coords& coords, const std::optional blocks = std::nullopt) noexcept; shared::math::coords get_pos() const noexcept { return this->pos; } block get_block(const glm::ivec3& v) const noexcept { return this->blocks[get_3d_index(v)]; } block& get_block(const glm::ivec3& v) noexcept { return this->blocks[get_3d_index(v)]; } }; } // namespace world } // namespace shared #endif