diff --git a/src/main/java/cam72cam/immersiverailroading/Config.java b/src/main/java/cam72cam/immersiverailroading/Config.java index a70eb6016..c27a661b9 100644 --- a/src/main/java/cam72cam/immersiverailroading/Config.java +++ b/src/main/java/cam72cam/immersiverailroading/Config.java @@ -301,6 +301,12 @@ public static class ConfigDebug { @Comment("Does stock drops itself/components when player is in creative mode?") public static boolean stockDropInCreativeMode = true; + + @Comment("Enable track snapping") + public static boolean enableTrackSnapping = false; + + @Comment("Should track snapping also snap angle") + public static boolean trackSnapAngle = false; } public static boolean isFuelRequired(Gauge gauge) { diff --git a/src/main/java/cam72cam/immersiverailroading/items/ItemGoldenSpike.java b/src/main/java/cam72cam/immersiverailroading/items/ItemGoldenSpike.java index 6a6104f46..1a835690f 100644 --- a/src/main/java/cam72cam/immersiverailroading/items/ItemGoldenSpike.java +++ b/src/main/java/cam72cam/immersiverailroading/items/ItemGoldenSpike.java @@ -1,10 +1,12 @@ package cam72cam.immersiverailroading.items; +import cam72cam.immersiverailroading.Config; import cam72cam.immersiverailroading.IRBlocks; import cam72cam.immersiverailroading.ImmersiveRailroading; import cam72cam.immersiverailroading.library.GuiTypes; import cam72cam.immersiverailroading.tile.TileRailBase; import cam72cam.immersiverailroading.tile.TileRailPreview; +import cam72cam.immersiverailroading.util.TrackUtil; import cam72cam.immersiverailroading.util.BlockUtil; import cam72cam.immersiverailroading.util.PlacementInfo; import cam72cam.mod.item.*; @@ -49,21 +51,31 @@ public ClickResult onClickBlock(Player player, World world, Vec3i pos, Player.Ha d.write(); Audio.playSound(world, pos, StandardSound.BLOCK_ANVIL_PLACE, SoundCategory.BLOCKS, 0.5f, 0.2f); } else { - pos = pos.up(); Vec3i tepos = new Data(held).pos; if (tepos != null) { - if (BlockUtil.canBeReplaced(world, pos.down(), true)) { - if (!BlockUtil.isIRRail(world, pos.down()) || world.getBlockEntity(pos.down(), TileRailBase.class).getRailHeight() < 0.5) { - pos = pos.down(); - } - } TileRailPreview tr = world.getBlockEntity(tepos, TileRailPreview.class); if (tr != null) { + PlacementInfo info = TrackUtil.getNeighborNode(player, player.getWorld(), pos, hit, tr.getItem()); + boolean useSnapping = info != null && Config.ConfigDebug.enableTrackSnapping; + float yaw; + if(useSnapping) { + pos = new Vec3i(info.placementPosition); + hit = info.placementPosition.subtract(pos); + yaw = info.yaw; + } else { + pos = pos.up(); + if (BlockUtil.canBeReplaced(world, pos.down(), true)) { + if (!BlockUtil.isIRRail(world, pos.down()) || world.getBlockEntity(pos.down(), TileRailBase.class).getRailHeight() < 0.5) { + pos = pos.down(); + } + } + yaw = player.getRotationYawHead(); + } if (tr.isAboveRails()) { tepos = tepos.down(); } - tr.setCustomInfo(new PlacementInfo(tr.getItem(), player.getYawHead(), hit.subtract(0, hit.y, 0).add(pos).subtract(tepos))); + tr.setCustomInfo(new PlacementInfo(tr.getItem(), yaw, hit.subtract(0, hit.y, 0).add(pos).subtract(tepos), useSnapping)); } } } diff --git a/src/main/java/cam72cam/immersiverailroading/items/ItemTrackBlueprint.java b/src/main/java/cam72cam/immersiverailroading/items/ItemTrackBlueprint.java index b01611ba2..39f7457d4 100644 --- a/src/main/java/cam72cam/immersiverailroading/items/ItemTrackBlueprint.java +++ b/src/main/java/cam72cam/immersiverailroading/items/ItemTrackBlueprint.java @@ -1,5 +1,6 @@ package cam72cam.immersiverailroading.items; +import cam72cam.immersiverailroading.Config; import cam72cam.immersiverailroading.IRBlocks; import cam72cam.immersiverailroading.ImmersiveRailroading; import cam72cam.immersiverailroading.items.nbt.RailSettings; @@ -9,6 +10,7 @@ import cam72cam.immersiverailroading.registry.TrackDefinition; import cam72cam.immersiverailroading.tile.TileRailBase; import cam72cam.immersiverailroading.tile.TileRailPreview; +import cam72cam.immersiverailroading.util.TrackUtil; import cam72cam.immersiverailroading.util.BlockUtil; import cam72cam.immersiverailroading.util.IRFuzzy; import cam72cam.immersiverailroading.util.PlacementInfo; @@ -55,7 +57,9 @@ public void onClickAir(Player player, World world, Player.Hand hand) { @Override public ClickResult onClickBlock(Player player, World world, Vec3i pos, Player.Hand hand, Facing facing, Vec3d hit) { ItemStack stack = player.getHeldItem(hand); + PlacementInfo snapped = TrackUtil.getNeighborNode(player, player.getWorld(), pos, hit, stack); RailSettings stackInfo = RailSettings.from(stack); + float yaw; if (world.isServer && hand == Player.Hand.SECONDARY) { ItemStack blockinfo = world.getItemStack(pos); @@ -68,11 +72,19 @@ public ClickResult onClickBlock(Player player, World world, Vec3i pos, Player.Ha return ClickResult.ACCEPTED; } - pos = pos.up(); - - if (BlockUtil.canBeReplaced(world, pos.down(), true)) { - if (!BlockUtil.isIRRail(world, pos.down()) || world.getBlockEntity(pos.down(), TileRailBase.class).getRailHeight() < 0.5) { - pos = pos.down(); + boolean useSnapping = snapped != null && Config.ConfigDebug.enableTrackSnapping; + if(useSnapping) { + pos = new Vec3i(snapped.placementPosition); + hit = snapped.placementPosition.subtract(pos); + yaw = snapped.yaw; + } else { + pos = pos.up(); + yaw = player.getRotationYawHead(); + + if (BlockUtil.canBeReplaced(world, pos.down(), true)) { + if (!BlockUtil.isIRRail(world, pos.down()) || world.getBlockEntity(pos.down(), TileRailBase.class).getRailHeight() < 0.5) { + pos = pos.down(); + } } } @@ -83,13 +95,13 @@ public ClickResult onClickBlock(Player player, World world, Vec3i pos, Player.Ha world.setBlock(pos, IRBlocks.BLOCK_RAIL_PREVIEW); TileRailPreview te = world.getBlockEntity(pos, TileRailPreview.class); if (te != null) { - PlacementInfo placementInfo = new PlacementInfo(stack, player.getYawHead(), hit.subtract(0, hit.y, 0)); + PlacementInfo placementInfo = new PlacementInfo(stack, yaw, hit.subtract(0, hit.y, 0), useSnapping); te.setup(stack, placementInfo); } return ClickResult.ACCEPTED; } - PlacementInfo placementInfo = new PlacementInfo(stack, player.getYawHead(), hit.subtract(0, hit.y, 0)); + PlacementInfo placementInfo = new PlacementInfo(stack, yaw, hit.subtract(0, hit.y, 0), useSnapping); RailInfo info = new RailInfo(stack, placementInfo, null); info.build(player, pos); return ClickResult.ACCEPTED; diff --git a/src/main/java/cam72cam/immersiverailroading/render/item/TrackBlueprintItemModel.java b/src/main/java/cam72cam/immersiverailroading/render/item/TrackBlueprintItemModel.java index f65d35d72..09d708ad3 100644 --- a/src/main/java/cam72cam/immersiverailroading/render/item/TrackBlueprintItemModel.java +++ b/src/main/java/cam72cam/immersiverailroading/render/item/TrackBlueprintItemModel.java @@ -1,5 +1,7 @@ package cam72cam.immersiverailroading.render.item; +import cam72cam.immersiverailroading.Config; +import cam72cam.immersiverailroading.util.TrackUtil; import cam72cam.immersiverailroading.library.TrackItems; import cam72cam.immersiverailroading.render.ExpireableMap; import cam72cam.immersiverailroading.render.rail.RailRender; @@ -55,18 +57,33 @@ public static void render(ItemStack stack, World world, RenderState state) { private static ExpireableMap infoCache = new ExpireableMap<>(); public static void renderMouseover(Player player, ItemStack stack, Vec3i pos, Vec3d vec, RenderState state, float partialTicks) { - Vec3d hit = vec.subtract(pos); World world = player.getWorld(); + Vec3d cameraPos = GlobalRender.getCameraPos(partialTicks); + + PlacementInfo closeEnd = TrackUtil.getNeighborNode(player, world, pos, vec.subtract(pos), stack); + boolean useSnapping = closeEnd != null && Config.ConfigDebug.enableTrackSnapping; - pos = pos.up(); + Vec3d hit; + float yaw; - if (BlockUtil.canBeReplaced(world, pos.down(), true)) { - if (!BlockUtil.isIRRail(world, pos.down()) || world.getBlockEntity(pos.down(), TileRailBase.class).getRailHeight() < 0.5) { - pos = pos.down(); + if (!useSnapping) { + hit = vec.subtract(pos); + pos = pos.up(); + + if (BlockUtil.canBeReplaced(world, pos.down(), true)) { + if (!BlockUtil.isIRRail(world, pos.down()) || world.getBlockEntity(pos.down(), TileRailBase.class).getRailHeight() < 0.5) { + pos = pos.down(); + } } + + yaw = player.getRotationYawHead(); + } else { + pos = new Vec3i(closeEnd.placementPosition); + hit = closeEnd.placementPosition.subtract(pos); + yaw = closeEnd.yaw; } - RailInfo info = new RailInfo(stack, new PlacementInfo(stack, player.getRotationYawHead(), hit.subtract(0, hit.y, 0)), null); + RailInfo info = new RailInfo(stack, new PlacementInfo(stack, yaw, hit.subtract(0, hit.y, 0), useSnapping), null); String key = info.uniqueID + info.placementInfo.placementPosition; RailInfo cached = infoCache.get(key); if (cached != null) { @@ -77,8 +94,6 @@ public static void renderMouseover(Player player, ItemStack stack, Vec3i pos, Ve state.blend(new BlendMode(BlendMode.GL_CONSTANT_ALPHA, BlendMode.GL_ONE).constantColor(1, 1, 1, 0.5f)).lightmap(1, 1); - - Vec3d cameraPos = GlobalRender.getCameraPos(partialTicks); Vec3d offPos = info.placementInfo.placementPosition.add(pos).subtract(cameraPos); state.translate(offPos.x, offPos.y, offPos.z); diff --git a/src/main/java/cam72cam/immersiverailroading/util/PlacementInfo.java b/src/main/java/cam72cam/immersiverailroading/util/PlacementInfo.java index 5283417c7..5e12b3edf 100644 --- a/src/main/java/cam72cam/immersiverailroading/util/PlacementInfo.java +++ b/src/main/java/cam72cam/immersiverailroading/util/PlacementInfo.java @@ -30,6 +30,9 @@ public static int segmentation() { } public PlacementInfo(ItemStack stack, float yawHead, Vec3d hit) { + this(stack, yawHead, hit, false); + } + public PlacementInfo(ItemStack stack, float yawHead, Vec3d hit, boolean snapping) { yawHead = ((- yawHead % 360) + 360) % 360; this.yaw = ((int)((yawHead + 90/(segmentation() * 2f)) * segmentation())) / 90 * 90 / (segmentation() * 1f); @@ -44,30 +47,17 @@ public PlacementInfo(ItemStack stack, float yawHead, Vec3d hit) { double hitX = hit.x % 1; double hitZ = hit.z % 1; - - switch (settings.posType) { - case FIXED: - hitX = 0.5f; - hitZ = 0.5f; - break; - case PIXELS: - hitX = ((int) (hitX * 16)) / 16f; - hitZ = ((int) (hitZ * 16)) / 16f; - //Fall through - case SMOOTH: - if (hit.z < 0) { - hitZ += 1; - } - if (hit.x < 0) { - hitX += 1; - } - break; - case PIXELS_LOCKED: - hitX = ((int) (hitX * 16)) / 16f; - hitZ = ((int) (hitZ * 16)) / 16f; - //Fall through - case SMOOTH_LOCKED: - if (quarter != 0) { + if (!snapping) { + switch (settings.posType) { + case FIXED: + hitX = 0.5f; + hitZ = 0.5f; + break; + case PIXELS: + hitX = ((int) (hitX * 16)) / 16f; + hitZ = ((int) (hitZ * 16)) / 16f; + //Fall through + case SMOOTH: if (hit.z < 0) { hitZ += 1; } @@ -75,28 +65,41 @@ public PlacementInfo(ItemStack stack, float yawHead, Vec3d hit) { hitX += 1; } break; - } - - switch (facing()) { - case EAST: - case WEST: - hitZ = 0.5f; - if (hit.x < 0) { - hitX += 1; - } - break; - case NORTH: - case SOUTH: - hitX = 0.5f; + case PIXELS_LOCKED: + hitX = ((int) (hitX * 16)) / 16f; + hitZ = ((int) (hitZ * 16)) / 16f; + //Fall through + case SMOOTH_LOCKED: + if (quarter != 0) { if (hit.z < 0) { hitZ += 1; } + if (hit.x < 0) { + hitX += 1; + } break; - default: - } - break; - } + } + switch (facing()) { + case EAST: + case WEST: + hitZ = 0.5f; + if (hit.x < 0) { + hitX += 1; + } + break; + case NORTH: + case SOUTH: + hitX = 0.5f; + if (hit.z < 0) { + hitZ += 1; + } + break; + default: + } + break; + } + } this.placementPosition = new Vec3d(new Vec3i(hit)).add(hitX, 0, hitZ); this.direction = direction; this.control = null; diff --git a/src/main/java/cam72cam/immersiverailroading/util/TrackUtil.java b/src/main/java/cam72cam/immersiverailroading/util/TrackUtil.java new file mode 100644 index 000000000..d35892359 --- /dev/null +++ b/src/main/java/cam72cam/immersiverailroading/util/TrackUtil.java @@ -0,0 +1,95 @@ +package cam72cam.immersiverailroading.util; + +import cam72cam.immersiverailroading.Config; +import cam72cam.immersiverailroading.items.nbt.RailSettings; +import cam72cam.immersiverailroading.library.TrackDirection; +import cam72cam.immersiverailroading.tile.TileRail; +import cam72cam.immersiverailroading.tile.TileRailBase; +import cam72cam.immersiverailroading.track.BuilderBase; +import cam72cam.immersiverailroading.track.VecYPR; +import cam72cam.mod.entity.Player; +import cam72cam.mod.item.ItemStack; +import cam72cam.mod.math.Vec3d; +import cam72cam.mod.math.Vec3i; +import cam72cam.mod.world.World; + +import java.util.List; + +public class TrackUtil { + public static PlacementInfo getNeighborNode(Player player, World world, Vec3i pos, Vec3d hit, ItemStack stack) { + RailSettings stackInfo = RailSettings.from(stack); + Vec3d worldPos = new Vec3d(pos).add(hit); + Vec3d minPos = worldPos; + double min = Double.MAX_VALUE; + int hori = Math.max((int) (stackInfo.gauge.scale() * 2), 1); + int vert = 1; + float yaw = player.getRotationYaw(); + float rotationYawHead = (player.getRotationYawHead() + 360f) % 360f; + + for (int x = -hori; x <= hori; x++) { + for (int y = -vert; y <= vert; y++) { + for (int z = -hori; z <= hori; z++) { + Vec3i offset = pos.add(x, y, z); + TileRailBase tile = world.getBlockEntity(offset, TileRailBase.class); + while (tile != null){ + if (!(tile instanceof TileRail)) { + tile = tile.getParentTile(); + } + + TileRail rail = (TileRail) tile; + if (rail == null || rail.info == null || + Math.abs(rail.getTrackGauge() - stackInfo.gauge.value()) > 1.0E-6) continue; + + BuilderBase builder = rail.info.getBuilder(world); + List renderData = builder.getRenderData(); + if (renderData.isEmpty()) continue; + + if (renderData.size() > 1) { + Vec3d p1 = renderData.get(0).add(rail.info.placementInfo.placementPosition).add( + tile.getPos()); + float yaw1 = VecUtil.toYaw(renderData.get(1).subtract(renderData.get(0))); + double dist1 = p1.distanceTo(worldPos); + if (dist1 < min) { + min = dist1; + minPos = p1; + yaw = yaw1; + } + + Vec3d p2 = renderData.get(renderData.size() - 1) + .add(rail.info.placementInfo.placementPosition).add(tile.getPos()); + float yaw2 = VecUtil.toYaw(renderData.get(renderData.size() - 2).subtract( + renderData.get(renderData.size() - 1))); + double dist = p2.distanceTo(worldPos); + if (dist < min) { + min = dist; + minPos = p2; + yaw = yaw2; + } + } else { + Vec3d p = renderData.get(0).add(rail.info.placementInfo.placementPosition).add( + tile.getPos()); + float currentYaw = renderData.get(0).getYaw(); + if (Math.abs(currentYaw - rotationYawHead) > 90) { + currentYaw += 180; + } + double dist = p.distanceTo(worldPos); + if (dist < min) { + min = dist; + minPos = p; + yaw = currentYaw; + } + } + + tile = tile.getReplacedTile(); + } + } + } + } + + if (min <= hori) { + yaw = Config.ConfigDebug.trackSnapAngle ? (540 - yaw) % 360 : rotationYawHead; + return new PlacementInfo(minPos, TrackDirection.NONE, yaw, null); + } + return null; + } +}