minecraft-src/net/minecraft/client/renderer/item/properties/numeric/CompassAngleState.java
2025-07-04 03:45:38 +03:00

120 lines
4.3 KiB
Java

package net.minecraft.client.renderer.item.properties.numeric;
import com.mojang.serialization.Codec;
import com.mojang.serialization.MapCodec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.client.renderer.item.properties.numeric.NeedleDirectionHelper.Wobbler;
import net.minecraft.core.BlockPos;
import net.minecraft.core.GlobalPos;
import net.minecraft.util.Mth;
import net.minecraft.util.RandomSource;
import net.minecraft.util.StringRepresentable;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.phys.Vec3;
import org.jetbrains.annotations.Nullable;
@Environment(EnvType.CLIENT)
public class CompassAngleState extends NeedleDirectionHelper {
public static final MapCodec<CompassAngleState> MAP_CODEC = RecordCodecBuilder.mapCodec(
instance -> instance.group(
Codec.BOOL.optionalFieldOf("wobble", true).forGetter(NeedleDirectionHelper::wobble),
CompassAngleState.CompassTarget.CODEC.fieldOf("target").forGetter(CompassAngleState::target)
)
.apply(instance, CompassAngleState::new)
);
private final Wobbler wobbler;
private final Wobbler noTargetWobbler;
private final CompassAngleState.CompassTarget compassTarget;
private final RandomSource random = RandomSource.create();
public CompassAngleState(boolean wobble, CompassAngleState.CompassTarget compassTarget) {
super(wobble);
this.wobbler = this.newWobbler(0.8F);
this.noTargetWobbler = this.newWobbler(0.8F);
this.compassTarget = compassTarget;
}
@Override
protected float calculate(ItemStack stack, ClientLevel level, int seed, Entity entity) {
GlobalPos globalPos = this.compassTarget.get(level, stack, entity);
long l = level.getGameTime();
return !isValidCompassTargetPos(entity, globalPos)
? this.getRandomlySpinningRotation(seed, l)
: this.getRotationTowardsCompassTarget(entity, l, globalPos.pos());
}
private float getRandomlySpinningRotation(int seed, long gameTime) {
if (this.noTargetWobbler.shouldUpdate(gameTime)) {
this.noTargetWobbler.update(gameTime, this.random.nextFloat());
}
float f = this.noTargetWobbler.rotation() + hash(seed) / 2.1474836E9F;
return Mth.positiveModulo(f, 1.0F);
}
private float getRotationTowardsCompassTarget(Entity entity, long gameTime, BlockPos targetPos) {
float f = (float)getAngleFromEntityToPos(entity, targetPos);
float g = getWrappedVisualRotationY(entity);
float h;
if (entity instanceof Player player && player.isLocalPlayer() && player.level().tickRateManager().runsNormally()) {
if (this.wobbler.shouldUpdate(gameTime)) {
this.wobbler.update(gameTime, 0.5F - (g - 0.25F));
}
h = f + this.wobbler.rotation();
} else {
h = 0.5F - (g - 0.25F - f);
}
return Mth.positiveModulo(h, 1.0F);
}
private static boolean isValidCompassTargetPos(Entity entity, @Nullable GlobalPos pos) {
return pos != null && pos.dimension() == entity.level().dimension() && !(pos.pos().distToCenterSqr(entity.position()) < 1.0E-5F);
}
private static double getAngleFromEntityToPos(Entity entity, BlockPos pos) {
Vec3 vec3 = Vec3.atCenterOf(pos);
return Math.atan2(vec3.z() - entity.getZ(), vec3.x() - entity.getX()) / (float) (Math.PI * 2);
}
private static float getWrappedVisualRotationY(Entity entity) {
return Mth.positiveModulo(entity.getVisualRotationYInDegrees() / 360.0F, 1.0F);
}
private static int hash(int seed) {
return seed * 1327217883;
}
protected CompassAngleState.CompassTarget target() {
return this.compassTarget;
}
@Environment(EnvType.CLIENT)
public static enum CompassTarget implements StringRepresentable {
NONE("NONE", 0, "none"),
LODESTONE("LODESTONE", 1, "lodestone"),
SPAWN("SPAWN", 2, "spawn"),
RECOVERY("RECOVERY", 3, "recovery");
public static final Codec<CompassAngleState.CompassTarget> CODEC = StringRepresentable.fromEnum(CompassAngleState.CompassTarget::values);
private final String name;
CompassTarget(final String name) {
this.name = name;
}
@Override
public String getSerializedName() {
return this.name;
}
@Nullable
abstract GlobalPos get(ClientLevel level, ItemStack stack, Entity entity);
}
}