#include "client/movement/movement.hh" namespace client { namespace movement { static std::optional maybe_get_block(const shared::math::coords& pos, const client::world::chunks_t& chunks, const glm::ivec3& block_pos) noexcept { const auto find_it = chunks.find(pos); if (find_it == std::end(chunks)) { return std::nullopt; } if (!find_it->second.has_value()) { return std::nullopt; } return find_it->second->get_block(block_pos); } static std::optional maybe_make_blocks(const glm::vec3& local_pos, const shared::math::coords& chunk_pos, const client::world::chunks_t& chunks, const int width, const int height) noexcept { shared::movement::blocks blocks; for (int x = -width; x <= width; ++x) { for (int y = -height; y <= height; ++y) { for (int z = -width; z <= width; ++z) { const glm::ivec3 rel_pos = glm::ivec3{x, y, z} + glm::ivec3{local_pos}; if (rel_pos.y < 0 || rel_pos.y >= shared::world::chunk::HEIGHT) { continue; } const shared::math::coords norm_chunk_pos = shared::world::chunk::get_normalised_chunk( chunk_pos, rel_pos.x, rel_pos.z); const glm::ivec3 norm_pos = shared::world::chunk::get_normalised_coords(rel_pos); const auto block = maybe_get_block(norm_chunk_pos, chunks, norm_pos); if (!block.has_value()) { return std::nullopt; } const glm::vec3 pos = glm::vec3{rel_pos} - local_pos; const shared::movement::aabb aabb = { .min = glm::vec3{0.0f, 0.0f, 0.0f} + pos, .max = glm::vec3{1.0f, 1.0f, 1.0f} + pos}; blocks.push_back( shared::movement::block{.block = *block, .aabb = aabb, .chunk_pos = norm_chunk_pos, .pos = norm_pos}); } } } return blocks; } std::optional move(const shared::moveable& moveable, const world::chunks_t& chunks) noexcept { const auto xy = shared::movement::get_move_xy(state::tickrate, moveable); const auto blocks = maybe_make_blocks( moveable.get_local_pos(), moveable.get_chunk_pos(), chunks, xy.x, xy.y); if (!blocks.has_value()) { return std::nullopt; } return shared::movement::move(moveable, *blocks, state::tickrate); } std::optional> interact(const shared::moveable& moveable, const interact_mode& mode, const client::world::chunks_t& chunks) noexcept { const auto blocks = maybe_make_blocks( moveable.get_local_pos(), moveable.get_chunk_pos(), chunks, 5, 5); if (!blocks.has_value()) { return std::nullopt; } constexpr float epsilon = 0.001f; const auto& aabb = moveable.get_aabb(); constexpr shared::movement::aabb ray_aabb = { .min = {-epsilon, shared::player::EYE_HEIGHT - epsilon, -epsilon}, .max = {epsilon, shared::player::EYE_HEIGHT + epsilon, epsilon}}; const shared::movement::moving_aabb ray_moving_aabb{ .aabb = ray_aabb, .velocity = moveable.get_angles().to_dir()}; std::optional< std::pair> best; for (const auto& block : *blocks) { if (block.block == shared::world::block::type::air) { continue; } if (!shared::world::block::is_removable(block.block)) { continue; } const auto intersect = shared::movement::intersect_moving_aabbs( ray_moving_aabb, shared::movement::moving_aabb{.aabb = block.aabb, .velocity = {0.0f, 0.0f, 0.0f}}); constexpr float MAX_REACH = 3.8f; if (!intersect.has_value() || intersect->time > MAX_REACH) { continue; } if (best.has_value() && intersect->time > best->first.time) { continue; } best = std::make_pair(*intersect, block); } if (!best.has_value()) { return std::nullopt; } const auto& [intersect, block] = *best; const glm::ivec3 position = block.pos; if (mode == interact_mode::remove) { return std::make_pair(block.chunk_pos, position); } const glm::ivec3 normal_i = -intersect.normal; const glm::ivec3 normalised_pos = shared::world::chunk::get_normalised_coords(normal_i + position); const auto find_it = std::find_if(std::begin(*blocks), std::end(*blocks), [&](const auto b) { return b.pos == normalised_pos; }); if (find_it == std::end(*blocks)) { return std::nullopt; } if (!shared::world::block::is_replaceable(find_it->block)) { return std::nullopt; } if (shared::movement::intersect_aabbs(aabb, find_it->aabb)) { return std::nullopt; } return std::make_pair(find_it->chunk_pos, normalised_pos); } } // namespace movement } // namespace client