Freelook zoom

This commit is contained in:
2026-03-26 16:26:49 +01:00
parent 3e4985c6bc
commit 7eb8b6a120
@@ -1,49 +1,59 @@
package org.saturnclient.mod.mods;
import java.util.function.Function;
import org.saturnclient.common.provider.Providers;
import org.saturnclient.common.provider.GLFWProvider;
import org.saturnclient.config.property.BoolProperty;
import org.saturnclient.config.property.FloatProperty;
import org.saturnclient.config.property.IntProperty;
import org.saturnclient.config.property.KeybindingProperty;
import org.saturnclient.config.property.Property;
import org.saturnclient.config.property.SelectProperty;
import org.saturnclient.mod.Mod;
import org.saturnclient.mod.ModSpec;
import org.saturnclient.common.feature.RenderFeature;
import org.saturnclient.common.provider.GLFWProvider;
import org.saturnclient.ui.anim.Animation;
/**
* FreelookMod lets the player look around freely (in third-person)
* without changing the direction their character walks.
*
* Camera perspective is controlled through {@link RenderFeature} so
* that the feature itself contains no direct Minecraft API calls.
*/
public class FreelookMod extends Mod {
private static final BoolProperty enabled = Property.bool(false);
private static final BoolProperty toggle = Property.bool(true);
public static final KeybindingProperty freelookKey = Property.keybinding(GLFWProvider.GLFW_KEY_H);
/** Whether freelook is currently active (read by the camera mixin). */
/** Zoom properties for freelook */
public static final FloatProperty zoomLevel = Property.floatProp(2.0f); // 2x zoom out
public static final IntProperty zoomInDuration = Property.integer(500); // ms
public static final IntProperty zoomOutDuration = Property.integer(300); // ms
public static final SelectProperty curve = Property.select(1, "Ease In Out Cubic", "Ease Out Cubic",
"Ease In Out Back");
/** Whether freelook is currently active */
public static boolean isFreeLooking = false;
private static boolean wasFirstPerson = false;
/** Animation state */
private static long animationStartTime = System.currentTimeMillis();
public FreelookMod() {
super(
new ModSpec("Freelook", "freelook")
.description("Look around freely without moving your character")
.tags("Movement", "Camera")
.version("v0.2.0")
.version("v0.3.1")
.requires(Providers.feature::render),
enabled.named("Enabled"),
toggle.named("Toggle freelook"),
freelookKey.named("Freelook Keybinding"));
freelookKey.named("Freelook Keybinding"),
zoomLevel.named("Zoom Level"),
zoomInDuration.named("Zoom In Duration"),
zoomOutDuration.named("Zoom Out Duration"),
curve.named("Curve"));
}
// ---------------------------------------------------------------
// Mod contract
// ---------------------------------------------------------------
@Override
public void tick() {
// Only update freelook state — do NOT touch animation here
if (toggle.value) {
if (freelookKey.wasKeyPressed()) {
if (isFreeLooking)
@@ -71,15 +81,12 @@ public class FreelookMod extends Mod {
return enabled.value;
}
// ---------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------
private void startFreelook() {
RenderFeature render = Providers.feature.render();
wasFirstPerson = render.isFirstPerson();
render.setThirdPersonBack();
isFreeLooking = true;
animationStartTime = System.currentTimeMillis();
}
private void stopFreelook() {
@@ -87,5 +94,43 @@ public class FreelookMod extends Mod {
Providers.feature.render().setFirstPerson();
}
isFreeLooking = false;
animationStartTime = System.currentTimeMillis();
}
/** Returns true if we should apply zoom out effect */
public static boolean shouldZoomOut() {
return enabled.value && (isFreeLooking || getProgress() > 0f);
}
/** Returns the current zoom divisor (1 = normal, >1 = zoomed out) */
public static float getZoomLevel() {
float progress = getProgress();
// Apply curve
Function<Double, Double> curveFn = Animation.getCurve(curve.value);
double curved = curveFn.apply((double) progress);
// Map 0 → zoomLevel, 1 → normal FOV
return zoomLevel.value - (float) curved * (zoomLevel.value - 1f);
}
/** Returns animation progress 0 → 1 based on system time */
private static float getProgress() {
boolean target = isFreeLooking;
int duration = target ? zoomInDuration.value : zoomOutDuration.value;
long now = System.currentTimeMillis();
float rawProgress = (now - animationStartTime) / (float) duration;
if (target) {
return clamp(rawProgress, 0f, 1f);
} else {
// Reverse for zoom out
return clamp(1f - rawProgress, 0f, 1f);
}
}
private static float clamp(float value, float min, float max) {
return Math.max(min, Math.min(max, value));
}
}