refactor(aim): split RotationManager to 199 lines
State machine, mutable state and server-rotation packet tracking move to rotation/internal/; the singleton, event wiring and public API stay put.
This commit is contained in:
parent
7e8b6b6467
commit
3e57542bad
4 changed files with 329 additions and 228 deletions
|
|
@ -1,57 +1,39 @@
|
|||
package dev.loki.lovisual.util.aiming.rotation;
|
||||
import dev.loki.lovisual.util.aiming.RequestHandler;
|
||||
|
||||
|
||||
import dev.loki.lovisual.util.screen.ClientScreen;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen;
|
||||
import net.minecraft.client.player.LocalPlayer;
|
||||
import net.minecraft.network.protocol.game.ServerboundMovePlayerPacket;
|
||||
import net.minecraft.network.protocol.game.ServerboundUseItemPacket;
|
||||
import net.minecraft.util.Mth;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import dev.loki.lovisual.events.meta.EventHandler;
|
||||
import dev.loki.lovisual.events.Events;
|
||||
import dev.loki.lovisual.events.impl.input.RotationUpdateEvent;
|
||||
import dev.loki.lovisual.events.impl.network.EventSync;
|
||||
import dev.loki.lovisual.events.impl.network.PacketEvent;
|
||||
import dev.loki.lovisual.events.impl.world.PlayerVelocityStrafe;
|
||||
import dev.loki.lovisual.events.impl.input.RotationUpdateEvent;
|
||||
import dev.loki.lovisual.events.meta.EventHandler;
|
||||
import dev.loki.lovisual.mixininterface.entity.ILocalPlayer;
|
||||
import dev.loki.lovisual.mixins.accessors.entity.EntityInvoker;
|
||||
import dev.loki.lovisual.util.aiming.data.Rotation;
|
||||
import dev.loki.lovisual.util.aiming.features.MovementCorrection;
|
||||
import dev.loki.lovisual.util.aiming.rotation.internal.RotationPackets;
|
||||
import dev.loki.lovisual.util.aiming.rotation.internal.RotationState;
|
||||
import dev.loki.lovisual.util.aiming.rotation.internal.RotationStepper;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.player.LocalPlayer;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
|
||||
/**
|
||||
* Public rotation facade: singleton entry point, event wiring and accessors.
|
||||
* The state machine lives in {@link RotationStepper}, state in
|
||||
* {@link RotationState} and server-rotation tracking in {@link RotationPackets}.
|
||||
*/
|
||||
public final class RotationManager {
|
||||
|
||||
public static final RotationManager INSTANCE = new RotationManager();
|
||||
|
||||
private final RequestHandler<RotationTarget> rotationTargetHandler = new RequestHandler<>();
|
||||
private RotationTarget previousRotationTarget;
|
||||
private Object previousRotationProvider;
|
||||
|
||||
private Rotation currentRotation;
|
||||
private Rotation previousRotation;
|
||||
private Object currentRotationProvider;
|
||||
private boolean smoothReturnActive;
|
||||
private int smoothReturnTicks;
|
||||
|
||||
private Rotation actualServerRotation = Rotation.ZERO;
|
||||
private Rotation theoreticalServerRotation = Rotation.ZERO;
|
||||
private final RotationState state = new RotationState();
|
||||
private final RotationPackets packets = new RotationPackets();
|
||||
private int lastLifecycleAge = Integer.MIN_VALUE;
|
||||
|
||||
private RotationManager() {
|
||||
}
|
||||
|
||||
private static float computeRotationDifference(Rotation a, Rotation b) {
|
||||
if (a == null || b == null) {
|
||||
return Float.MAX_VALUE;
|
||||
}
|
||||
float yawDiff = Math.abs(RotationUtil.angleDifference(a.yaw(), b.yaw()));
|
||||
float pitchDiff = Math.abs(a.pitch() - b.pitch());
|
||||
return (float) Math.hypot(yawDiff, pitchDiff);
|
||||
}
|
||||
|
||||
public static LocalPlayer player() {
|
||||
return Minecraft.getInstance().player;
|
||||
}
|
||||
|
|
@ -85,78 +67,48 @@ public final class RotationManager {
|
|||
}
|
||||
|
||||
public RotationTarget getActiveRotationTarget() {
|
||||
RotationTarget active = rotationTargetHandler.getActiveRequestValue();
|
||||
return active != null ? active : previousRotationTarget;
|
||||
return state.activeTarget();
|
||||
}
|
||||
|
||||
public RotationTarget getPreviousRotationTarget() {
|
||||
return previousRotationTarget;
|
||||
return state.previousTarget();
|
||||
}
|
||||
|
||||
public Rotation getCurrentRotation() {
|
||||
return currentRotation;
|
||||
}
|
||||
|
||||
private void setCurrentRotation(Rotation rotation) {
|
||||
setCurrentRotation(rotation, currentRotationProvider);
|
||||
}
|
||||
|
||||
private void setCurrentRotation(Rotation rotation, Object provider) {
|
||||
if (rotation == null) {
|
||||
if (currentRotation != null) {
|
||||
previousRotation = currentRotation;
|
||||
}
|
||||
currentRotation = null;
|
||||
currentRotationProvider = null;
|
||||
return;
|
||||
}
|
||||
|
||||
previousRotation = currentRotation;
|
||||
currentRotation = rotation;
|
||||
currentRotationProvider = provider;
|
||||
return state.current();
|
||||
}
|
||||
|
||||
public Rotation getPreviousRotation() {
|
||||
return previousRotation;
|
||||
return state.previous();
|
||||
}
|
||||
|
||||
public Rotation getServerRotation() {
|
||||
return actualServerRotation;
|
||||
return packets.actual();
|
||||
}
|
||||
|
||||
public Rotation getMovementRotation() {
|
||||
LocalPlayer player = player();
|
||||
Rotation current = state.current();
|
||||
Rotation fallback = player != null
|
||||
? new Rotation(player.getYRot(), player.getXRot(), true)
|
||||
: (currentRotation != null ? currentRotation : Rotation.ZERO);
|
||||
: (current != null ? current : Rotation.ZERO);
|
||||
|
||||
RotationTarget active = getActiveRotationTarget();
|
||||
if (currentRotation == null || active == null || active.movementCorrection == MovementCorrection.OFF) {
|
||||
if (current == null || active == null || active.movementCorrection == MovementCorrection.OFF) {
|
||||
return fallback;
|
||||
}
|
||||
|
||||
return currentRotation;
|
||||
return current;
|
||||
}
|
||||
|
||||
public void setRotationTarget(RotationTarget plan, int priority, Object provider) {
|
||||
if (plan == null) return;
|
||||
|
||||
rotationTargetHandler.request(new RequestHandler.Request<>(plan.ticksUntilReset, priority, provider, plan));
|
||||
smoothReturnActive = false;
|
||||
smoothReturnTicks = 0;
|
||||
state.request(plan, priority, provider);
|
||||
}
|
||||
|
||||
public void setRotationTarget(RotationTarget plan, int priority) {
|
||||
setRotationTarget(plan, priority, null);
|
||||
}
|
||||
|
||||
private boolean isRotatingAllowed(RotationTarget target) {
|
||||
if (!target.considerInventory) return true;
|
||||
Minecraft mc = Minecraft.getInstance();
|
||||
if (mc == null) return true;
|
||||
return !(ClientScreen.current() instanceof AbstractContainerScreen<?>);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update current rotation to a new rotation step.
|
||||
*/
|
||||
|
|
@ -167,124 +119,15 @@ public final class RotationManager {
|
|||
return;
|
||||
}
|
||||
|
||||
Rotation playerRotation = new Rotation(player.getYRot(), player.getXRot(), true);
|
||||
RequestHandler.Request<RotationTarget> activeRequest = rotationTargetHandler.getActiveRequest();
|
||||
RotationTarget activeRotationTarget = activeRequest != null ? activeRequest.value() : previousRotationTarget;
|
||||
Object activeProvider = activeRequest != null ? activeRequest.provider() : previousRotationProvider;
|
||||
|
||||
if (activeRotationTarget == null) {
|
||||
if (currentRotation == null || !smoothReturnActive) {
|
||||
return;
|
||||
}
|
||||
|
||||
float diffToPlayer = computeRotationDifference(currentRotation, playerRotation);
|
||||
if (diffToPlayer <= 0.5f) {
|
||||
finishSmoothReturn();
|
||||
return;
|
||||
}
|
||||
|
||||
float speed = 0.25f + 0.4f * Math.min(1.0f, diffToPlayer / 30.0f);
|
||||
float yawDiff = RotationUtil.angleDifference(playerRotation.yaw(), currentRotation.yaw());
|
||||
float newYaw = currentRotation.yaw() + yawDiff * speed;
|
||||
float newPitch = Mth.lerp(speed, currentRotation.pitch(), playerRotation.pitch());
|
||||
setCurrentRotation(new Rotation(newYaw, newPitch, false).normalize(), currentRotationProvider);
|
||||
return;
|
||||
}
|
||||
|
||||
if (isRotatingAllowed(activeRotationTarget)) {
|
||||
Rotation fromRotation = resolveBaseRotation(playerRotation);
|
||||
if (activeRequest == null) {
|
||||
if (currentRotation == null) {
|
||||
finishSmoothReturn();
|
||||
return;
|
||||
}
|
||||
if (!smoothReturnActive) {
|
||||
smoothReturnActive = true;
|
||||
smoothReturnTicks = 0;
|
||||
}
|
||||
|
||||
Rotation resetRotation = activeRotationTarget.towards(fromRotation, true).normalize();
|
||||
setCurrentRotation(resetRotation, activeProvider);
|
||||
previousRotationTarget = activeRotationTarget;
|
||||
previousRotationProvider = activeProvider;
|
||||
|
||||
smoothReturnTicks++;
|
||||
float diffToPlayer = computeRotationDifference(resetRotation, playerRotation);
|
||||
int maxTicks = Math.max(1, activeRotationTarget.ticksUntilReset);
|
||||
float threshold = Math.max(0.05f, activeRotationTarget.resetThreshold);
|
||||
if (diffToPlayer <= threshold || smoothReturnTicks >= maxTicks) {
|
||||
finishSmoothReturn();
|
||||
rotationTargetHandler.tick();
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
Rotation rotation = activeRotationTarget.towards(fromRotation, false).normalize();
|
||||
setCurrentRotation(rotation, activeProvider);
|
||||
previousRotationTarget = activeRotationTarget;
|
||||
previousRotationProvider = activeProvider;
|
||||
smoothReturnActive = false;
|
||||
smoothReturnTicks = 0;
|
||||
|
||||
if (activeRotationTarget.whenReached != null) {
|
||||
activeRotationTarget.whenReached.invoke();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rotationTargetHandler.tick();
|
||||
}
|
||||
|
||||
private void finishSmoothReturn() {
|
||||
setCurrentRotation(null);
|
||||
previousRotation = null;
|
||||
previousRotationTarget = null;
|
||||
previousRotationProvider = null;
|
||||
smoothReturnActive = false;
|
||||
smoothReturnTicks = 0;
|
||||
}
|
||||
|
||||
private Rotation resolveBaseRotation(Rotation playerRotation) {
|
||||
if (currentRotation != null) {
|
||||
return currentRotation;
|
||||
}
|
||||
if (actualServerRotation != null && actualServerRotation != Rotation.ZERO) {
|
||||
return actualServerRotation;
|
||||
}
|
||||
return playerRotation;
|
||||
RotationStepper.step(state, player, packets.actual());
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
rotationTargetHandler.clear();
|
||||
previousRotationTarget = null;
|
||||
previousRotationProvider = null;
|
||||
currentRotation = null;
|
||||
currentRotationProvider = null;
|
||||
previousRotation = null;
|
||||
smoothReturnActive = false;
|
||||
smoothReturnTicks = 0;
|
||||
state.clearAll();
|
||||
}
|
||||
|
||||
public void clear(Object provider) {
|
||||
if (provider == null) return;
|
||||
boolean removed = rotationTargetHandler.clear(provider);
|
||||
boolean ownsVisualState = provider == currentRotationProvider || provider == previousRotationProvider;
|
||||
if (!removed && !ownsVisualState) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (currentRotation != null && ownsVisualState) {
|
||||
smoothReturnActive = true;
|
||||
smoothReturnTicks = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (ownsVisualState) {
|
||||
previousRotationTarget = null;
|
||||
previousRotationProvider = null;
|
||||
previousRotation = null;
|
||||
}
|
||||
smoothReturnActive = false;
|
||||
smoothReturnTicks = 0;
|
||||
state.clearProvider(provider);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -296,33 +139,16 @@ public final class RotationManager {
|
|||
}
|
||||
|
||||
public void release(Object provider, boolean smoothReturn) {
|
||||
if (provider == null) return;
|
||||
boolean removed = rotationTargetHandler.clear(provider);
|
||||
boolean ownsVisualState = provider == currentRotationProvider || provider == previousRotationProvider;
|
||||
if (!removed && !ownsVisualState) {
|
||||
return;
|
||||
}
|
||||
if (!smoothReturn) {
|
||||
if (ownsVisualState) {
|
||||
setCurrentRotation(null);
|
||||
previousRotationTarget = null;
|
||||
previousRotationProvider = null;
|
||||
previousRotation = null;
|
||||
}
|
||||
smoothReturnActive = false;
|
||||
smoothReturnTicks = 0;
|
||||
return;
|
||||
}
|
||||
smoothReturnActive = ownsVisualState && currentRotation != null;
|
||||
smoothReturnTicks = 0;
|
||||
state.releaseProvider(provider, smoothReturn);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onSync(EventSync e) {
|
||||
RotationTarget activeRotationTarget = getActiveRotationTarget();
|
||||
if (activeRotationTarget == null || currentRotation == null) return;
|
||||
RotationTarget active = state.activeTarget();
|
||||
Rotation current = state.current();
|
||||
if (active == null || current == null) return;
|
||||
|
||||
e.setRotation(currentRotation.yaw(), currentRotation.pitch(), true);
|
||||
e.setRotation(current.yaw(), current.pitch(), true);
|
||||
}
|
||||
|
||||
@EventHandler(priority = 100)
|
||||
|
|
@ -346,30 +172,12 @@ public final class RotationManager {
|
|||
|
||||
@EventHandler(priority = -100)
|
||||
public void onPacketSend(PacketEvent.Send e) {
|
||||
if (e.isCancelled()) return;
|
||||
var packet = e.getPacket();
|
||||
|
||||
Rotation rot = null;
|
||||
if (packet instanceof ServerboundMovePlayerPacket move) {
|
||||
if (!move.hasRotation()) return;
|
||||
rot = new Rotation(move.getYRot(0.0f), move.getXRot(0.0f), true);
|
||||
} else if (packet instanceof ServerboundUseItemPacket use) {
|
||||
rot = new Rotation(use.getYRot(), use.getXRot(), true);
|
||||
}
|
||||
|
||||
if (rot != null) {
|
||||
actualServerRotation = rot;
|
||||
theoreticalServerRotation = rot;
|
||||
}
|
||||
packets.onSend(e);
|
||||
}
|
||||
|
||||
@EventHandler(priority = -100)
|
||||
public void onPacketReceive(PacketEvent.Receive e) {
|
||||
if (!(e.getPacket() instanceof net.minecraft.network.protocol.game.ClientboundPlayerPositionPacket packet))
|
||||
return;
|
||||
Rotation rot = new Rotation(packet.change().yRot(), packet.change().xRot(), true);
|
||||
theoreticalServerRotation = rot;
|
||||
actualServerRotation = rot;
|
||||
packets.onReceive(e);
|
||||
}
|
||||
|
||||
public void runTickLifecycle() {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,50 @@
|
|||
package dev.loki.lovisual.util.aiming.rotation.internal;
|
||||
|
||||
import dev.loki.lovisual.events.impl.network.PacketEvent;
|
||||
import dev.loki.lovisual.util.aiming.data.Rotation;
|
||||
import net.minecraft.network.protocol.game.ClientboundPlayerPositionPacket;
|
||||
import net.minecraft.network.protocol.game.ServerboundMovePlayerPacket;
|
||||
import net.minecraft.network.protocol.game.ServerboundUseItemPacket;
|
||||
|
||||
/**
|
||||
* Tracks the rotation the server sees, updated from outbound and inbound
|
||||
* movement packets. Kept apart from the facade so packet wiring stays small.
|
||||
*/
|
||||
public final class RotationPackets {
|
||||
|
||||
private Rotation actualServerRotation = Rotation.ZERO;
|
||||
private Rotation theoreticalServerRotation = Rotation.ZERO;
|
||||
|
||||
public Rotation actual() {
|
||||
return actualServerRotation;
|
||||
}
|
||||
|
||||
public void onSend(PacketEvent.Send e) {
|
||||
if (e.isCancelled()) return;
|
||||
var packet = e.getPacket();
|
||||
|
||||
Rotation rot = null;
|
||||
if (packet instanceof ServerboundMovePlayerPacket move) {
|
||||
if (!move.hasRotation()) return;
|
||||
rot = new Rotation(move.getYRot(0.0f), move.getXRot(0.0f), true);
|
||||
} else if (packet instanceof ServerboundUseItemPacket use) {
|
||||
rot = new Rotation(use.getYRot(), use.getXRot(), true);
|
||||
}
|
||||
|
||||
if (rot != null) {
|
||||
actualServerRotation = rot;
|
||||
theoreticalServerRotation = rot;
|
||||
}
|
||||
}
|
||||
|
||||
public void onReceive(PacketEvent.Receive e) {
|
||||
if (!(e.getPacket() instanceof ClientboundPlayerPositionPacket packet)) return;
|
||||
Rotation rot = new Rotation(packet.change().yRot(), packet.change().xRot(), true);
|
||||
theoreticalServerRotation = rot;
|
||||
actualServerRotation = rot;
|
||||
}
|
||||
|
||||
public Rotation theoretical() {
|
||||
return theoreticalServerRotation;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,128 @@
|
|||
package dev.loki.lovisual.util.aiming.rotation.internal;
|
||||
|
||||
import dev.loki.lovisual.util.aiming.RequestHandler;
|
||||
import dev.loki.lovisual.util.aiming.data.Rotation;
|
||||
import dev.loki.lovisual.util.aiming.rotation.RotationTarget;
|
||||
|
||||
/**
|
||||
* Mutable rotation state shared by {@link RotationStepper} and the public
|
||||
* {@code RotationManager} facade. Fields are package-private on purpose so the
|
||||
* stepper can drive them in place, mirroring the original single-class logic.
|
||||
*/
|
||||
public final class RotationState {
|
||||
|
||||
final RequestHandler<RotationTarget> handler = new RequestHandler<>();
|
||||
|
||||
RotationTarget previousTarget;
|
||||
Object previousProvider;
|
||||
Rotation current;
|
||||
Rotation previous;
|
||||
Object currentProvider;
|
||||
boolean smoothReturnActive;
|
||||
int smoothReturnTicks;
|
||||
|
||||
public RotationTarget activeTarget() {
|
||||
RotationTarget active = handler.getActiveRequestValue();
|
||||
return active != null ? active : previousTarget;
|
||||
}
|
||||
|
||||
public RotationTarget previousTarget() {
|
||||
return previousTarget;
|
||||
}
|
||||
|
||||
public Rotation current() {
|
||||
return current;
|
||||
}
|
||||
|
||||
public Rotation previous() {
|
||||
return previous;
|
||||
}
|
||||
|
||||
public void request(RotationTarget plan, int priority, Object provider) {
|
||||
if (plan == null) return;
|
||||
|
||||
handler.request(new RequestHandler.Request<>(plan.ticksUntilReset, priority, provider, plan));
|
||||
smoothReturnActive = false;
|
||||
smoothReturnTicks = 0;
|
||||
}
|
||||
|
||||
void setCurrent(Rotation rotation, Object provider) {
|
||||
if (rotation == null) {
|
||||
if (current != null) {
|
||||
previous = current;
|
||||
}
|
||||
current = null;
|
||||
currentProvider = null;
|
||||
return;
|
||||
}
|
||||
|
||||
previous = current;
|
||||
current = rotation;
|
||||
currentProvider = provider;
|
||||
}
|
||||
|
||||
void finishSmoothReturn() {
|
||||
setCurrent(null, currentProvider);
|
||||
previous = null;
|
||||
previousTarget = null;
|
||||
previousProvider = null;
|
||||
smoothReturnActive = false;
|
||||
smoothReturnTicks = 0;
|
||||
}
|
||||
|
||||
public void clearAll() {
|
||||
handler.clear();
|
||||
previousTarget = null;
|
||||
previousProvider = null;
|
||||
current = null;
|
||||
currentProvider = null;
|
||||
previous = null;
|
||||
smoothReturnActive = false;
|
||||
smoothReturnTicks = 0;
|
||||
}
|
||||
|
||||
public void clearProvider(Object provider) {
|
||||
if (provider == null) return;
|
||||
boolean removed = handler.clear(provider);
|
||||
boolean ownsVisualState = provider == currentProvider || provider == previousProvider;
|
||||
if (!removed && !ownsVisualState) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (current != null && ownsVisualState) {
|
||||
smoothReturnActive = true;
|
||||
smoothReturnTicks = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (ownsVisualState) {
|
||||
previousTarget = null;
|
||||
previousProvider = null;
|
||||
previous = null;
|
||||
}
|
||||
smoothReturnActive = false;
|
||||
smoothReturnTicks = 0;
|
||||
}
|
||||
|
||||
public void releaseProvider(Object provider, boolean smoothReturn) {
|
||||
if (provider == null) return;
|
||||
boolean removed = handler.clear(provider);
|
||||
boolean ownsVisualState = provider == currentProvider || provider == previousProvider;
|
||||
if (!removed && !ownsVisualState) {
|
||||
return;
|
||||
}
|
||||
if (!smoothReturn) {
|
||||
if (ownsVisualState) {
|
||||
setCurrent(null, currentProvider);
|
||||
previousTarget = null;
|
||||
previousProvider = null;
|
||||
previous = null;
|
||||
}
|
||||
smoothReturnActive = false;
|
||||
smoothReturnTicks = 0;
|
||||
return;
|
||||
}
|
||||
smoothReturnActive = ownsVisualState && current != null;
|
||||
smoothReturnTicks = 0;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,115 @@
|
|||
package dev.loki.lovisual.util.aiming.rotation.internal;
|
||||
|
||||
import dev.loki.lovisual.util.aiming.RequestHandler;
|
||||
import dev.loki.lovisual.util.aiming.data.Rotation;
|
||||
import dev.loki.lovisual.util.aiming.rotation.RotationTarget;
|
||||
import dev.loki.lovisual.util.aiming.rotation.RotationUtil;
|
||||
import dev.loki.lovisual.util.screen.ClientScreen;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen;
|
||||
import net.minecraft.client.player.LocalPlayer;
|
||||
import net.minecraft.util.Mth;
|
||||
|
||||
/**
|
||||
* Per-tick rotation state machine: resolves the active target, advances the
|
||||
* current rotation and drives the smooth return back to the player view.
|
||||
*/
|
||||
public final class RotationStepper {
|
||||
|
||||
private RotationStepper() {
|
||||
}
|
||||
|
||||
public static void step(RotationState state, LocalPlayer player, Rotation serverRotation) {
|
||||
Rotation playerRotation = new Rotation(player.getYRot(), player.getXRot(), true);
|
||||
RequestHandler.Request<RotationTarget> activeRequest = state.handler.getActiveRequest();
|
||||
RotationTarget activeRotationTarget = activeRequest != null ? activeRequest.value() : state.previousTarget;
|
||||
Object activeProvider = activeRequest != null ? activeRequest.provider() : state.previousProvider;
|
||||
|
||||
if (activeRotationTarget == null) {
|
||||
if (state.current == null || !state.smoothReturnActive) {
|
||||
return;
|
||||
}
|
||||
|
||||
float diffToPlayer = computeRotationDifference(state.current, playerRotation);
|
||||
if (diffToPlayer <= 0.5f) {
|
||||
state.finishSmoothReturn();
|
||||
return;
|
||||
}
|
||||
|
||||
float speed = 0.25f + 0.4f * Math.min(1.0f, diffToPlayer / 30.0f);
|
||||
float yawDiff = RotationUtil.angleDifference(playerRotation.yaw(), state.current.yaw());
|
||||
float newYaw = state.current.yaw() + yawDiff * speed;
|
||||
float newPitch = Mth.lerp(speed, state.current.pitch(), playerRotation.pitch());
|
||||
state.setCurrent(new Rotation(newYaw, newPitch, false).normalize(), state.currentProvider);
|
||||
return;
|
||||
}
|
||||
|
||||
if (isRotatingAllowed(activeRotationTarget)) {
|
||||
Rotation fromRotation = resolveBaseRotation(state, playerRotation, serverRotation);
|
||||
if (activeRequest == null) {
|
||||
if (state.current == null) {
|
||||
state.finishSmoothReturn();
|
||||
return;
|
||||
}
|
||||
if (!state.smoothReturnActive) {
|
||||
state.smoothReturnActive = true;
|
||||
state.smoothReturnTicks = 0;
|
||||
}
|
||||
|
||||
Rotation resetRotation = activeRotationTarget.towards(fromRotation, true).normalize();
|
||||
state.setCurrent(resetRotation, activeProvider);
|
||||
state.previousTarget = activeRotationTarget;
|
||||
state.previousProvider = activeProvider;
|
||||
|
||||
state.smoothReturnTicks++;
|
||||
float diffToPlayer = computeRotationDifference(resetRotation, playerRotation);
|
||||
int maxTicks = Math.max(1, activeRotationTarget.ticksUntilReset);
|
||||
float threshold = Math.max(0.05f, activeRotationTarget.resetThreshold);
|
||||
if (diffToPlayer <= threshold || state.smoothReturnTicks >= maxTicks) {
|
||||
state.finishSmoothReturn();
|
||||
state.handler.tick();
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
Rotation rotation = activeRotationTarget.towards(fromRotation, false).normalize();
|
||||
state.setCurrent(rotation, activeProvider);
|
||||
state.previousTarget = activeRotationTarget;
|
||||
state.previousProvider = activeProvider;
|
||||
state.smoothReturnActive = false;
|
||||
state.smoothReturnTicks = 0;
|
||||
|
||||
if (activeRotationTarget.whenReached != null) {
|
||||
activeRotationTarget.whenReached.invoke();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
state.handler.tick();
|
||||
}
|
||||
|
||||
private static float computeRotationDifference(Rotation a, Rotation b) {
|
||||
if (a == null || b == null) {
|
||||
return Float.MAX_VALUE;
|
||||
}
|
||||
float yawDiff = Math.abs(RotationUtil.angleDifference(a.yaw(), b.yaw()));
|
||||
float pitchDiff = Math.abs(a.pitch() - b.pitch());
|
||||
return (float) Math.hypot(yawDiff, pitchDiff);
|
||||
}
|
||||
|
||||
private static boolean isRotatingAllowed(RotationTarget target) {
|
||||
if (!target.considerInventory) return true;
|
||||
Minecraft mc = Minecraft.getInstance();
|
||||
if (mc == null) return true;
|
||||
return !(ClientScreen.current() instanceof AbstractContainerScreen<?>);
|
||||
}
|
||||
|
||||
private static Rotation resolveBaseRotation(RotationState state, Rotation playerRotation, Rotation serverRotation) {
|
||||
if (state.current != null) {
|
||||
return state.current;
|
||||
}
|
||||
if (serverRotation != null && serverRotation != Rotation.ZERO) {
|
||||
return serverRotation;
|
||||
}
|
||||
return playerRotation;
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue