Skip to content

Let ParCool see sub-levels - #1418

Open
BlackCube4 wants to merge 1 commit into
ryanhcode:mainfrom
BlackCube4:fix/parcool-sublevel-collision
Open

Let ParCool see sub-levels#1418
BlackCube4 wants to merge 1 commit into
ryanhcode:mainfrom
BlackCube4:fix/parcool-sublevel-collision

Conversation

@BlackCube4

Copy link
Copy Markdown

None of its parkour moves function on a physics object today.
Wall-running, wall-jumping, vaulting and ledge grabs all fail.

Cause

Every wall, ledge and step ParCool looks for is found the same way: build a box around the player,
expand it toward a candidate surface, and ask whether anything is in the way.

// ParCool, WorldUtil#getWall - and getRunnableWall, getVaultableStep, getGrabbableWall alike
if (boxes.stream().noneMatch(box -> entity.level().noCollision(box.expandTowards(range, 0.0, 0.0)))) {
    wallX++;
}

There is no raycast anywhere in WorldUtil - 31 probes across the class, every one of them a
Level#noCollision call.

Sable makes collision work on a sub-level by redirecting the specific vanilla call sites that
matter - Entity#collide while moving, Player#canFallAtLeast, and so on. Level#noCollision
itself is untouched, and entity_sublevel_collision.LevelMixin only carries the JOML sink. So a
mod that reaches for noCollision directly gets vanilla's answer: the world where the structure
appears to be is empty air, the structure is invisible to it, and ParCool concludes there is no
wall to run along and no ledge to grab.

Strictly this is not a ParCool bug - any mod probing collision that way is blind to sub-levels in
the same manner.

Fix

compatibility/parcool/WorldUtilMixin redirects both noCollision overloads across the class
through a sub-level check. It sits under mixin.compatibility.parcool, so
AbstractSableMixinPlugin already gates it on ParCool being loaded, and it needs no compile
dependency on ParCool: every type in the redirected signatures is vanilla, so the mixin targets by
name.

Redirecting the whole class rather than a chosen method is deliberate. The actions share one blind
spot, and a per-action list would silently miss whatever ParCool adds next. Vanilla is still asked
first, so this can only ever turn a "clear" answer into an "occupied" one - it cannot invent a wall
that neither the level nor a sub-level has.

Why a new helper rather than CanFallAtleastHelper

The geometry is deliberately the same - Sable.HELPER.getAllIntersecting, transformInverse into
the sub-level's frame, then OrientedBoundingBox3d.sat per collision box - but the existing helper
answers a different question:

localBounds.minY -= 1.0; // fences

It grows the query box downward and drops its lower bound by a block so a fence post still counts
as ground underfoot. That is right for "is there anything to land on" and wrong for "is there a
wall here", where it would report a wall a metre above any floor. Hence SubLevelNoCollisionHelper

  • the same traversal with honest box bounds.

Testing

ParCool + Create Aeronautics, parkour actions enabled:

  1. Build a wall a few blocks high on a physics object and assemble it.
  2. Run at the wall, wall-run and wall-jump do nothing before this change, and behave as they do on
    ordinary terrain after it.
  3. Jump at the top edge of that wall - likewise the ledge grab.
  4. Vault a one-block step on the same structure.

One area I would welcome a second opinion on: the query box is transformed into the sub-level's
frame and tested with SAT, so a rotating or steeply banked structure is the interesting case
rather than a level one. If the probe boxes want tightening there, SubLevelNoCollisionHelper
keeps them at full size, where CanFallAtleastHelper shrinks its equivalent by 0.1 on X and Z.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant