aboutsummaryrefslogtreecommitdiff
path: root/src/client/movement.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/movement.cc')
-rw-r--r--src/client/movement.cc165
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