diff --git a/common/src/main/java/dev/ryanhcode/sable/api/entity/TargetLocalInteractionEntity.java b/common/src/main/java/dev/ryanhcode/sable/api/entity/TargetLocalInteractionEntity.java new file mode 100644 index 000000000..8e6de8566 --- /dev/null +++ b/common/src/main/java/dev/ryanhcode/sable/api/entity/TargetLocalInteractionEntity.java @@ -0,0 +1,20 @@ +package dev.ryanhcode.sable.api.entity; + +import dev.ryanhcode.sable.sublevel.SubLevel; + +/** + * Implemented by synthetic interaction entities whose position and rotation + * may already be expressed in a target SubLevel's local coordinate system. + * + *
When this returns {@code true}, Sable must not inverse-transform the + * entity into the same SubLevel a second time.
+ */ +public interface TargetLocalInteractionEntity { + + /** + * @param targetSubLevel the SubLevel containing the interaction target + * @return true when this entity is already expressed in the target's + * local coordinate system + */ + boolean sable$isAlreadyLocalTo(SubLevel targetSubLevel); +} \ No newline at end of file diff --git a/common/src/main/java/dev/ryanhcode/sable/mixin/block_placement/BlockPlaceContextMixin.java b/common/src/main/java/dev/ryanhcode/sable/mixin/block_placement/BlockPlaceContextMixin.java index 408b3535f..0b8f43af3 100644 --- a/common/src/main/java/dev/ryanhcode/sable/mixin/block_placement/BlockPlaceContextMixin.java +++ b/common/src/main/java/dev/ryanhcode/sable/mixin/block_placement/BlockPlaceContextMixin.java @@ -2,6 +2,7 @@ import dev.ryanhcode.sable.Sable; import dev.ryanhcode.sable.api.SubLevelHelper; +import dev.ryanhcode.sable.api.entity.TargetLocalInteractionEntity; import dev.ryanhcode.sable.companion.math.BoundingBox3d; import dev.ryanhcode.sable.companion.math.BoundingBox3dc; import dev.ryanhcode.sable.api.math.LevelReusedVectors; @@ -40,6 +41,16 @@ public abstract class BlockPlaceContextMixin extends UseOnContext { @Shadow protected boolean replaceClicked; + @Unique + private static boolean sable$isAlreadyTargetLocal( + final Entity entity, + final SubLevel targetSubLevel + ) { + return entity instanceof + final TargetLocalInteractionEntity localEntity + && localEntity.sable$isAlreadyLocalTo(targetSubLevel); + } + public BlockPlaceContextMixin(final Player pPlayer, final InteractionHand pHand, final BlockHitResult pHitResult) { super(pPlayer, pHand, pHitResult); } @@ -51,6 +62,7 @@ public BlockPlaceContextMixin(final Player pPlayer, final InteractionHand pHand, private Direction sable$getFacingAxis(final Entity player, final Direction.Axis axis) { final SubLevel subLevel = Sable.HELPER.getContaining(this.getLevel(), this.getClickedPos()); + if(subLevel==null||sable$isAlreadyTargetLocal(player, subLevel)) return Direction.getFacingAxis(player,axis); if (subLevel != null) { SubLevelHelper.pushEntityLocal(subLevel, player); final Direction facingAxis = Direction.getFacingAxis(player, axis); @@ -65,14 +77,13 @@ public BlockPlaceContextMixin(final Player pPlayer, final InteractionHand pHand, private Direction[] sable$orderedByNearest(final Entity player) { final SubLevel subLevel = Sable.HELPER.getContaining(this.getLevel(), this.getClickedPos()); - if (subLevel != null) { - SubLevelHelper.pushEntityLocal(subLevel, player); - final Direction[] nearest = Direction.orderedByNearest(player); - SubLevelHelper.popEntityLocal(subLevel, player); - return nearest; - } + if (subLevel == null || sable$isAlreadyTargetLocal(player, subLevel)) return Direction.orderedByNearest(player); + + SubLevelHelper.pushEntityLocal(subLevel, player); + final Direction[] nearest = Direction.orderedByNearest(player); + SubLevelHelper.popEntityLocal(subLevel, player); + return nearest; - return Direction.orderedByNearest(player); } @Inject(method = "canPlace", at = @At("HEAD"), cancellable = true) diff --git a/common/src/main/java/dev/ryanhcode/sable/mixin/block_placement/UseOnContextMixin.java b/common/src/main/java/dev/ryanhcode/sable/mixin/block_placement/UseOnContextMixin.java index 589b83e76..4c6d42f8c 100644 --- a/common/src/main/java/dev/ryanhcode/sable/mixin/block_placement/UseOnContextMixin.java +++ b/common/src/main/java/dev/ryanhcode/sable/mixin/block_placement/UseOnContextMixin.java @@ -3,16 +3,19 @@ import dev.ryanhcode.sable.Sable; import dev.ryanhcode.sable.api.SubLevelHelper; +import dev.ryanhcode.sable.api.entity.TargetLocalInteractionEntity; import dev.ryanhcode.sable.sublevel.SubLevel; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.context.UseOnContext; import net.minecraft.world.level.Level; +import net.minecraft.world.phys.Vec3; import org.jetbrains.annotations.Nullable; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; @@ -34,18 +37,70 @@ public abstract class UseOnContextMixin { @Shadow public abstract BlockPos getClickedPos(); + @Unique + private boolean sable$isAlreadyTargetLocal( + final SubLevel targetSubLevel + ) { + return this.player instanceof + final TargetLocalInteractionEntity localEntity + && localEntity.sable$isAlreadyLocalTo(targetSubLevel); + } + + @Inject( + method = "getClickedFace", + at = @At("HEAD"), + cancellable = true + ) + private void sable$getClickedFace( + final CallbackInfoReturnable