diff options
| author | Nicolas James <Eele1Ephe7uZahRie@tutanota.com> | 2025-02-12 18:05:18 +1100 |
|---|---|---|
| committer | Nicolas James <Eele1Ephe7uZahRie@tutanota.com> | 2025-02-12 18:05:18 +1100 |
| commit | 1cc08c51eb4b0f95c30c0a98ad1fc5ad3459b2df (patch) | |
| tree | 222dfcd07a1e40716127a347bbfd7119ce3d0984 /src/client/movement.cc | |
initial commit
Diffstat (limited to 'src/client/movement.cc')
| -rw-r--r-- | src/client/movement.cc | 165 |
1 files changed, 165 insertions, 0 deletions
diff --git a/src/client/movement.cc b/src/client/movement.cc new file mode 100644 index 0000000..e5898eb --- /dev/null +++ b/src/client/movement.cc @@ -0,0 +1,165 @@ +#include "client/movement.hh" + +namespace client { +namespace movement { + +static std::optional<shared::world::block> +maybe_get_block(const shared::math::coords& pos, + const client::world::chunk::map& 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<std::vector<shared::movement::blockinfo>> +make_blockinfos(const shared::player& player, + const client::world::chunk::map& chunks, const int width, + const int height) noexcept { + + std::vector<shared::movement::blockinfo> blockinfos; + + 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{player.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( + player.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} - player.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}; + blockinfos.push_back( + shared::movement::blockinfo{.block = block.value(), + .aabb = aabb, + .chunk_pos = norm_chunk_pos, + .pos = norm_pos}); + } + } + } + + return blockinfos; +} + +void move(shared::player& player, client::world::chunk::map& chunks, + const float deltatime) noexcept { + const auto blockinfos = + make_blockinfos(player, chunks, shared::movement::move_width, + shared::movement::move_height); + if (!blockinfos.has_value()) { + return; + } + + shared::movement::move(player, blockinfos.value(), deltatime); +} + +std::optional<std::pair<shared::math::coords, glm::ivec3>> +interact(const shared::player& player, const interact_mode& mode, + const client::world::chunk::map& chunks) noexcept { + const auto blockinfos = make_blockinfos(player, chunks, 5, 5); + if (!blockinfos.has_value()) { + return std::nullopt; + } + + constexpr float epsilon = 0.001f; + constexpr shared::movement::aabb player_aabb = { + .min = {-shared::player::HALFWIDTH + epsilon, 0.0f + epsilon, + -shared::player::HALFWIDTH + epsilon}, + .max = {shared::player::HALFWIDTH - epsilon, + shared::player::HEIGHT - epsilon, + shared::player::HALFWIDTH - epsilon}}; + 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 = shared::math::angle_to_dir(player.viewangles)}; + + std::optional<std::pair<shared::movement::moving_aabb_ret, + shared::movement::blockinfo>> + best; + for (const auto& blockinfo : *blockinfos) { + if (blockinfo.block == shared::world::block::type::air) { + continue; + } + if (!shared::world::block::is_removable(blockinfo.block)) { + continue; + } + + const auto intersect = shared::movement::intersect_moving_aabbs( + ray_moving_aabb, + shared::movement::moving_aabb{.aabb = blockinfo.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, blockinfo); + } + + if (!best.has_value()) { + return std::nullopt; + } + + const auto& [intersect, blockinfo] = *best; + + const glm::ivec3 position = blockinfo.pos; + if (mode == interact_mode::remove) { + return std::make_pair(blockinfo.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(*blockinfos), std::end(*blockinfos), + [&](const auto b) { return b.pos == normalised_pos; }); + if (find_it == std::end(*blockinfos)) { + return std::nullopt; + } + if (!shared::world::block::is_replaceable(find_it->block)) { + return std::nullopt; + } + if (shared::movement::intersect_aabbs(player_aabb, find_it->aabb)) { + return std::nullopt; + } + return std::make_pair(find_it->chunk_pos, normalised_pos); +} + +} // namespace movement +} // namespace client |
