Working freelook

This commit is contained in:
2026-03-26 17:59:29 +01:00
parent 7eb8b6a120
commit d73895ac7c
@@ -17,22 +17,22 @@ import org.saturnclient.ui.anim.Animation;
public class FreelookMod extends Mod { public class FreelookMod extends Mod {
// --- Properties ---
private static final BoolProperty enabled = Property.bool(false); private static final BoolProperty enabled = Property.bool(false);
private static final BoolProperty toggle = Property.bool(true); private static final BoolProperty toggle = Property.bool(true);
public static final KeybindingProperty freelookKey = Property.keybinding(GLFWProvider.GLFW_KEY_H); private static final KeybindingProperty freelookKey = Property.keybinding(GLFWProvider.GLFW_KEY_H);
/** Zoom properties for freelook */ public static final FloatProperty zoomLevel = Property.floatProp(2.0f);
public static final FloatProperty zoomLevel = Property.floatProp(2.0f); // 2x zoom out public static final IntProperty zoomInDuration = Property.integer(500);
public static final IntProperty zoomInDuration = Property.integer(500); // ms public static final IntProperty zoomOutDuration = Property.integer(300);
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");
public static final SelectProperty curve = Property.select(1, "Ease In Out Cubic", "Ease Out Cubic",
"Ease In Out Back");
/** Whether freelook is currently active */ // --- State Variables ---
public static boolean isFreeLooking = false; private static boolean isFreeLooking = false;
private static boolean targetFreeLook = false;
private static boolean wasFirstPerson = false; private static boolean wasFirstPerson = false;
/** Animation state */ // --- Animation State ---
private static long animationStartTime = System.currentTimeMillis(); private static long animationStartTime = System.currentTimeMillis();
public FreelookMod() { public FreelookMod() {
@@ -51,34 +51,55 @@ public class FreelookMod extends Mod {
curve.named("Curve")); curve.named("Curve"));
} }
// --- Lifecycle Methods ---
@Override @Override
public void tick() { public boolean isEnabled() {
// Only update freelook state — do NOT touch animation here return enabled.value;
if (toggle.value) {
if (freelookKey.wasKeyPressed()) {
if (isFreeLooking)
stopFreelook();
else
startFreelook();
}
} else if (freelookKey.isKeyPressed()) {
if (!isFreeLooking)
startFreelook();
} else if (isFreeLooking) {
stopFreelook();
}
} }
@Override @Override
public void onEnabled(boolean e) { public void onEnabled(boolean e) {
enabled.value = e; enabled.value = e;
if (!e && isFreeLooking) if (!e && isFreeLooking) {
stopFreelook(); stopFreelook();
} }
}
@Override @Override
public boolean isEnabled() { public void tick() {
return enabled.value; handleInput();
// If we are returning to normal and animation finished, snap back to 1st person
if (!targetFreeLook && wasFirstPerson && getProgress() <= 0.0f) {
Providers.feature.render().setFirstPerson();
wasFirstPerson = false;
}
}
// --- Internal Logic ---
private void handleInput() {
if (toggle.value) {
if (freelookKey.wasKeyPressed()) {
targetFreeLook = !targetFreeLook;
toggleFreelookState(targetFreeLook);
}
} else {
boolean isDown = freelookKey.isKeyPressed();
if (isDown != targetFreeLook) { // state changed
targetFreeLook = isDown;
toggleFreelookState(targetFreeLook);
}
}
}
private void toggleFreelookState(boolean active) {
if (active) {
startFreelook();
} else {
stopFreelook();
}
} }
private void startFreelook() { private void startFreelook() {
@@ -90,47 +111,44 @@ public class FreelookMod extends Mod {
} }
private void stopFreelook() { private void stopFreelook() {
if (wasFirstPerson) { if (isFreeLooking) {
Providers.feature.render().setFirstPerson();
}
isFreeLooking = false; isFreeLooking = false;
animationStartTime = System.currentTimeMillis(); 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) */ // --- Math Utilities ---
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() { private static float getProgress() {
boolean target = isFreeLooking; if (!wasFirstPerson) return 0.0f;
int duration = target ? zoomInDuration.value : zoomOutDuration.value;
int duration = isFreeLooking ? zoomInDuration.value : zoomOutDuration.value;
long now = System.currentTimeMillis(); long now = System.currentTimeMillis();
float rawProgress = (now - animationStartTime) / (float) duration; float rawProgress = (now - animationStartTime) / (float) duration;
if (target) { return isFreeLooking ? clamp(rawProgress, 0.0f, 1.0f) : clamp(1.0f - rawProgress, 0.0f, 1.0f);
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) { private static float clamp(float value, float min, float max) {
return Math.max(min, Math.min(max, value)); return Math.max(min, Math.min(max, value));
} }
// --- Public API / Getters for External Hooks ---
public static boolean isFreeLooking() {
return isFreeLooking || getProgress() > 0;
}
public static boolean shouldZoomOut() {
return enabled.value && wasFirstPerson && isFreeLooking();
}
public static float getZoomLevel() {
float progress = getProgress();
Function<Double, Double> curveFn = Animation.getCurve(curve.value);
double curved = curveFn.apply((double) progress);
return zoomLevel.value - (float) curved * (zoomLevel.value - 1.0f);
}
} }