120 lines
4.3 KiB
Java
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);
|
|
}
|
|
}
|