diff --git a/src/client/PerlinInteraction.cpp b/src/client/PerlinInteraction.cpp index 655fe38..b960dcb 100644 --- a/src/client/PerlinInteraction.cpp +++ b/src/client/PerlinInteraction.cpp @@ -75,6 +75,21 @@ namespace perlin { }, {camera.x, camera.y, camera.z}, glm::dvec3(targeting::ViewVector(camera.yaw, camera.pitch)), PickRange(creative, 1), EntityPickRange(creative)); + // looking down past an edge with nothing in reach (pick 0x144E090C0): clip 2 blocks straight down + // from the eye; that block becomes the target, on the face of the look's dominant horizontal axis + const glm::dvec3 look(targeting::ViewVector(camera.yaw, camera.pitch)); + if (aimHit.kind == AimHit::Kind::None && look.y < -0.7) { + const std::vector noEntities; + AimHit below = Raycast( + live ? live->NetLevel() : *previewLevel, &levelRenderer->Blocks(), noEntities, + [](const entity::Entity&) { return glm::vec2(0.0f); }, {camera.x, camera.y, camera.z}, + glm::dvec3(0.0, -1.0, 0.0), 2.0, 0.0); + if (below.kind == AimHit::Kind::Block) { + below.face = std::fabs(look.x) > std::fabs(look.z) ? (look.x < 0.0 ? 4 : 5) : (look.z < 0.0 ? 2 : 3); + below.buildAssist = true; + aimHit = below; + } + } } if (PERLIN_ENV("PERLIN_AIM_DEBUG")) Log("aim", "kind {} block {},{},{} face {} dist {}", static_cast(aimHit.kind), aimHit.bx, @@ -313,6 +328,7 @@ namespace perlin { if (bedrockBuild && inputManager.IsDown(Action::Use) && !inputManager.Pressed(Action::Use)) { heldbuild::Hit hit; hit.block = aimHit.kind == AimHit::Kind::Block; + hit.assist = aimHit.buildAssist; hit.target = {{aimHit.bx, aimHit.by, aimHit.bz}, aimHit.face}; hit.point = aimHit.kind != AimHit::Kind::None ? glm::vec3(aimHit.point) : buildEye + glm::normalize(camera.Direction()) * diff --git a/src/client/player/HeldBuild.cpp b/src/client/player/HeldBuild.cpp index 113c363..3ef6e6c 100644 --- a/src/client/player/HeldBuild.cpp +++ b/src/client/player/HeldBuild.cpp @@ -106,7 +106,7 @@ namespace perlin::heldbuild { if (!SegmentHitsCell(eye, hit.point, nextBuild)) return std::nullopt; click = {nextBuild - direction, continueFacing}; - } else if (hit.block) { + } else if (hit.block && !hit.assist) { click = hit.target; const float xy = velocity.x * velocity.x + velocity.y * velocity.y; if (lastSnappable && !hasDirection && hasLastBuilt && velocity.z * velocity.z + xy > 0.0099999998f) { diff --git a/src/client/player/HeldBuild.hpp b/src/client/player/HeldBuild.hpp index 24e0176..9f6d0e7 100644 --- a/src/client/player/HeldBuild.hpp +++ b/src/client/player/HeldBuild.hpp @@ -21,6 +21,7 @@ namespace perlin::heldbuild { struct Hit { bool block = false; // crosshair on a block (direct hit) + bool assist = false; // the block is the look-down pick under the player, not a ray hit Target target; glm::vec3 point{}; // where the ray stopped: hit point, or eye + look * reach without a hit }; diff --git a/src/client/player/Interaction.hpp b/src/client/player/Interaction.hpp index 0d79339..02d0d5f 100644 --- a/src/client/player/Interaction.hpp +++ b/src/client/player/Interaction.hpp @@ -17,6 +17,8 @@ namespace perlin { uint64_t entity = 0; glm::dvec3 boxLo{0.0}, boxHi{0.0}; std::vector> shape; + // block under the player picked when looking down past an edge (HitResult +0x84): held repeats skip it + bool buildAssist = false; }; // world hit on the padded pick box, like HitResult::mPos: the game sends it as the entity click pos