diff --git a/src/main/java/moe/nea/firmament/mixins/GameRendererFovPatch.java b/src/main/java/moe/nea/firmament/mixins/GameRendererFovPatch.java new file mode 100644 index 00000000..12e4bc81 --- /dev/null +++ b/src/main/java/moe/nea/firmament/mixins/GameRendererFovPatch.java @@ -0,0 +1,25 @@ +package moe.nea.firmament.mixins; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import net.minecraft.client.renderer.GameRenderer; +import net.minecraft.client.Camera; + +@Mixin(GameRenderer.class) +public abstract class GameRendererFovPatch { + @Inject( + method = "getFov(Lnet/minecraft/client/Camera;FZ)F", + at = @At("RETURN"), + cancellable = true + ) + private void onGetFov(Camera camera, float tickDelta, boolean changingFov, CallbackInfoReturnable cir) { + try { + float base = cir.getReturnValueF(); + float mult = moe.nea.firmament.features.items.EtherwarpOverlay.getFovMultiplier(tickDelta); + cir.setReturnValue(base * mult); + } catch (Throwable t) { + } + } +} diff --git a/src/main/kotlin/features/items/EtherwarpOverlay.kt b/src/main/kotlin/features/items/EtherwarpOverlay.kt index a59fcbd3..bead6ad5 100644 --- a/src/main/kotlin/features/items/EtherwarpOverlay.kt +++ b/src/main/kotlin/features/items/EtherwarpOverlay.kt @@ -39,6 +39,8 @@ object EtherwarpOverlay { val tooFarCubeColour by colour("cube-colour-toofar") { ChromaColour.fromStaticRGB(255, 255, 0, 60) } var wireframe by toggle("wireframe") { false } var failureText by toggle("failure-text") { false } + var smoothZoom by toggle("smooth-zoom") { false } + val zoomSmoothness by integer("zoom-smoothness", 1, 100) { 50 } } enum class EtherwarpResult(val label: Component?, val color: () -> ChromaColour) { @@ -152,15 +154,6 @@ object EtherwarpOverlay { if (isEtherwarpTransparent(world, blockPos)) { return@traverseBlocks null } -// val defaultedState = world.getBlockState(blockPos).block.defaultState -// val hitShape = defaultedState.getCollisionShape( -// world, -// blockPos, -// ShapeContext.absent() -// ) -// if (world.raycastBlock(start, end, blockPos, hitShape, defaultedState) == null) { -// return@raycast null -// } val partialResult = world.clipWithInteractionOverride(start, end, blockPos, Shapes.block(), world.getBlockState(blockPos).block.defaultBlockState()) return@traverseBlocks EtherwarpBlockHit.BlockHit(blockPos, partialResult?.location) }, @@ -172,11 +165,41 @@ object EtherwarpOverlay { RAW } + // Smooth FOV state + private var currentFovMult = 1.0f + private var targetFovMult: Float = 1.0f + private var lastPartialTicks = 0f + + @JvmStatic + fun getFovMultiplier(partialTicks: Float): Float { + if (!TConfig.smoothZoom) { + currentFovMult = 1.0f + targetFovMult = 1.0f + lastPartialTicks = partialTicks + MC.currentTick + return 1.0f + } + + val partialDelta = (partialTicks + MC.currentTick) - lastPartialTicks + val pd = if (partialDelta <= 0f) 0.0f else partialDelta + lastPartialTicks = partialTicks + MC.currentTick + + val smooth = (TConfig.zoomSmoothness / 100f) + + val delta = targetFovMult - currentFovMult + currentFovMult += delta * pd * smooth + if (currentFovMult < 0.15f) currentFovMult = 0.15f + if (currentFovMult > 1f) currentFovMult = 1f + return currentFovMult + } + @Subscribe fun renderEtherwarpOverlay(event: WorldRenderLastEvent) { if (!TConfig.etherwarpOverlay) return val player = MC.player ?: return - if (TConfig.onlyShowWhileSneaking && !player.isShiftKeyDown) return + if (TConfig.onlyShowWhileSneaking && !player.isShiftKeyDown) { + targetFovMult = 1.0f + return + } val world = player.level val heldItem = MC.stackInHand val etherwarpTyp = run { @@ -184,10 +207,12 @@ object EtherwarpOverlay { EtherwarpItemKind.MERGED else if (heldItem.skyBlockId == SkyBlockItems.ETHERWARP_CONDUIT) EtherwarpItemKind.RAW - else + else { + targetFovMult = 1.0f return + } } - val playerEyeHeight = // Sneaking: 1.27 (1.21) 1.54 (1.8.9) / Upright: 1.62 (1.8.9,1.21) + val playerEyeHeight = // Sneaking: 1.27 (1.21) 1.54 (1.8.9) / Upright: 1.62 if (player.isShiftKeyDown || etherwarpTyp == EtherwarpItemKind.MERGED) (if (SBData.skyblockLocation?.isModernServer ?: false) 1.27 else 1.54) else 1.62 @@ -199,7 +224,10 @@ object EtherwarpOverlay { start, end, ) - if (hitResult !is EtherwarpBlockHit.BlockHit) return + if (hitResult !is EtherwarpBlockHit.BlockHit) { + targetFovMult = 1.0f + return + } val blockPos = hitResult.blockPos val success = run { if (!isEtherwarpTransparent(world, blockPos.above())) @@ -217,6 +245,7 @@ object EtherwarpOverlay { else EtherwarpResult.SUCCESS } + RenderInWorldContext.renderInWorld(event) { if (TConfig.cube) block( @@ -230,5 +259,16 @@ object EtherwarpOverlay { } } } + + if (success == EtherwarpResult.SUCCESS) { + val hitCenter = hitResult.accuratePos ?: blockPos.center + val dist = playerEyePos.distanceTo(hitCenter) + val distFactor = 1 - (kotlin.math.sqrt(dist * dist) / 60.0) + var newTarget = (distFactor * distFactor * distFactor * 0.75).toFloat() + 0.25f + if (newTarget < 0.25f) newTarget = 0.25f + targetFovMult = newTarget + } else { + targetFovMult = 1.0f + } } } diff --git a/translations/en_us.json b/translations/en_us.json index 31775b6c..1ae0cfb5 100644 --- a/translations/en_us.json +++ b/translations/en_us.json @@ -152,6 +152,10 @@ "firmament.config.etherwarp-overlay.failure-text.description": "Show a text in game explaining why the teleport fails.", "firmament.config.etherwarp-overlay.only-show-while-sneaking": "Only show while sneaking", "firmament.config.etherwarp-overlay.only-show-while-sneaking.description": "Displays the Etherwarp overlay only while sneaking.", + "firmament.config.etherwarp-overlay.smooth-zoom": "Smooth Zoom", + "firmament.config.etherwarp-overlay.smooth-zoom.description": "Enable smooth interpolation of FOV when Etherwarp targets a block", + "firmament.config.etherwarp-overlay.zoom-smoothness": "Zoom Smoothness", + "firmament.config.etherwarp-overlay.zoom-smoothness.description": "How quickly the FOV interpolates to the target (higher = snappier, lower = slower)", "firmament.config.etherwarp-overlay.wireframe": "Outline", "firmament.config.etherwarp-overlay.wireframe.description": "Displays a full outline on the block", "firmament.config.fairy-souls": "Fairy Souls", diff --git a/translations/languages/zh_cn.json b/translations/languages/zh_cn.json index 3612ea4c..c1b71b69 100644 --- a/translations/languages/zh_cn.json +++ b/translations/languages/zh_cn.json @@ -139,6 +139,10 @@ "firmament.config.etherwarp-overlay.only-show-while-sneaking.description": "仅在潜行时显示Etherwarp覆盖层。", "firmament.config.etherwarp-overlay.wireframe": "空心方框", "firmament.config.etherwarp-overlay.wireframe.description": "在指向方块上显示的轮廓", + "firmament.config.etherwarp-overlay.smooth-zoom": "平滑缩放", + "firmament.config.etherwarp-overlay.smooth-zoom.description": "当 Etherwarp 指向一个方块时启用 FOV 的平滑插值", + "firmament.config.etherwarp-overlay.zoom-smoothness": "缩放平滑度", + "firmament.config.etherwarp-overlay.zoom-smoothness.description": "FOV 插值到目标的速度(更高 = 更迅速,更低 = 更缓慢)", "firmament.config.fairy-souls": "仙女之魂", "firmament.config.fairy-souls.reset": "重置已收集仙女之魂", "firmament.config.fairy-souls.reset.description": "重置所有已收集的仙女之魂,允许你从头开始。",