diff --git a/src/main/java/com/minelittlepony/client/HorseCam.java b/src/main/java/com/minelittlepony/client/HorseCam.java new file mode 100644 index 00000000..b450ffa1 --- /dev/null +++ b/src/main/java/com/minelittlepony/client/HorseCam.java @@ -0,0 +1,94 @@ +package com.minelittlepony.client; + +import net.minecraft.client.MinecraftClient; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.util.hit.HitResult; +import net.minecraft.util.math.Vec3d; + +import com.minelittlepony.pony.IPony; +import com.minelittlepony.settings.PonySettings; + +public class HorseCam { + /** + * Transforms the client pony's pitch to the corresponding angle for a human character. + */ + public static float transformCameraAngle(float pitch) { + + if (!PonySettings.FILLYCAM.get()) { + return pitch; + } + + PlayerEntity player = MinecraftClient.getInstance().player; + IPony pony = MineLittlePony.getInstance().getManager().getPony(player); + + if (!pony.getRace(false).isHuman()) { + float factor = pony.getMetadata().getSize().getEyeHeightFactor(); + return rescaleCameraPitch(player.getStandingEyeHeight() / factor, pitch); + } + + return pitch; + } + + /** + * Calculates a corresponding camera pitch for the current player at + * the specified character height. + * + * @param toHeight Target height. + * @param originalPitch Original, unchanged pitch. + * + * @return The new pitch value, otherwise the original value passed in. + */ + public static float rescaleCameraPitch(double toHeight, float originalPitch) { + /* -90 + * | + * ---------------0 + * | + * 90 + */ + /* A A - toHeight + * |\ B - fromHeight + * |?\ y - headPitch + * | \ ? - result + * | \ C - raytrace + * B- \ + * |y - \ + * | - \ Tan(?) = horDist / toHeight + * |-------C ? = arcTan(horDist / toHeight); + * horDist + */ + MinecraftClient client = MinecraftClient.getInstance(); + PlayerEntity player = client.player; + HitResult hit = client.hitResult; + + // noop + if (hit == null || player == null) { + return originalPitch; + } + + // Small angles aren't worth changing. + // Helps with bows, arrows, and projectiles. + if (Math.abs(originalPitch) < 10) { + return originalPitch; + } + + Vec3d hitPos = hit.getPos(); + Vec3d pos = player.getPos(); + + double diffX = Math.abs(hitPos.x - pos.x); + double diffZ = Math.abs(hitPos.z - pos.z); + double horDist = Math.sqrt(diffX * diffX + diffZ * diffZ); + + float theta = (float)Math.atan(horDist / toHeight); + + // convert to degress + theta /= Math.PI / 180; + + // convert to vertical pitch (-90 to 90). + // Preserve up/down direction. + float newPitch = (90 - theta) * Math.signum(originalPitch); + + System.out.println("From " + originalPitch + " to " + newPitch); + + return newPitch; + } +} diff --git a/src/main/java/com/minelittlepony/client/mixin/MixinPlayerMoveC2SPacket_Both.java b/src/main/java/com/minelittlepony/client/mixin/MixinPlayerMoveC2SPacket_Both.java new file mode 100644 index 00000000..cabb07bc --- /dev/null +++ b/src/main/java/com/minelittlepony/client/mixin/MixinPlayerMoveC2SPacket_Both.java @@ -0,0 +1,19 @@ +package com.minelittlepony.client.mixin; + +import net.minecraft.server.network.packet.PlayerMoveC2SPacket; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import com.minelittlepony.client.HorseCam; + +@Mixin(PlayerMoveC2SPacket.Both.class) +public abstract class MixinPlayerMoveC2SPacket_Both extends PlayerMoveC2SPacket { + @Inject(method = "(DDDFFZ)V", + at = @At("RETURN")) + private void onInit(CallbackInfo info) { + this.pitch = HorseCam.transformCameraAngle(this.pitch); + } +} diff --git a/src/main/java/com/minelittlepony/client/mixin/MixinPlayerMoveC2SPacket_LookOnly.java b/src/main/java/com/minelittlepony/client/mixin/MixinPlayerMoveC2SPacket_LookOnly.java new file mode 100644 index 00000000..c6366b3b --- /dev/null +++ b/src/main/java/com/minelittlepony/client/mixin/MixinPlayerMoveC2SPacket_LookOnly.java @@ -0,0 +1,19 @@ +package com.minelittlepony.client.mixin; + +import net.minecraft.server.network.packet.PlayerMoveC2SPacket; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import com.minelittlepony.client.HorseCam; + +@Mixin(PlayerMoveC2SPacket.LookOnly.class) +public abstract class MixinPlayerMoveC2SPacket_LookOnly extends PlayerMoveC2SPacket { + @Inject(method = "(FFZ)V", + at = @At("RETURN")) + private void onInit(CallbackInfo info) { + this.pitch = HorseCam.transformCameraAngle(this.pitch); + } +} diff --git a/src/main/resources/minelp.mixin.json b/src/main/resources/minelp.mixin.json index 15f5d7ed..0be2a5d2 100644 --- a/src/main/resources/minelp.mixin.json +++ b/src/main/resources/minelp.mixin.json @@ -12,6 +12,8 @@ "MixinFirstPersonRenderer", "MixinGlStateManager", "MixinItemRenderer", - "MixinClientPlayerEntity" + "MixinClientPlayerEntity", + "MixinPlayerMoveC2SPacket_Both", + "MixinPlayerMoveC2SPacket_LookOnly" ] }