Initial commit

This commit is contained in:
2025-10-01 19:41:40 +02:00
commit 54e9d77665
18 changed files with 703 additions and 0 deletions

View File

@@ -0,0 +1,81 @@
package com.kasetoatz.oiia;
import com.kasetoatz.oiia.interfaces.ILivingEntity;
import net.fabricmc.api.ClientModInitializer;
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents;
import net.minecraft.client.network.ClientPlayerEntity;
import net.minecraft.client.sound.PositionedSoundInstance;
import net.minecraft.client.sound.SoundManager;
import net.minecraft.client.world.ClientWorld;
import net.minecraft.entity.Entity;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.projectile.ProjectileUtil;
import net.minecraft.predicate.entity.EntityPredicates;
import net.minecraft.sound.SoundCategory;
import net.minecraft.sound.SoundEvent;
import net.minecraft.util.Identifier;
import net.minecraft.util.hit.EntityHitResult;
import net.minecraft.util.hit.HitResult;
import net.minecraft.util.math.Box;
import net.minecraft.util.math.Vec3d;
public class Oiia implements ClientModInitializer
{
public static LivingEntity TARGET;
private HitResult raycast(Entity camera, int distance)
{
HitResult hitResult = camera.raycast(distance, 0F, false);
Vec3d start = camera.getCameraPosVec(0F);
double squaredDist = hitResult.getPos().squaredDistanceTo(start);
double newDist = Math.sqrt(squaredDist);
Vec3d rotation = camera.getRotationVec(0F);
Vec3d end = start.add(rotation.multiply(newDist));
Box box = camera.getBoundingBox().stretch(rotation.multiply(newDist)).expand(1D);
return ProjectileUtil.raycast(camera, start, end, box, EntityPredicates.CAN_HIT, squaredDist);
}
private void resetTarget(SoundManager manager)
{
if (TARGET != null)
{
ILivingEntity accessor = (ILivingEntity)TARGET;
accessor.oiia$setRotation(0F);
}
manager.stopSounds(Identifier.of("oiia", "oiia"), SoundCategory.RECORDS);
TARGET = null;
}
@Override
public void onInitializeClient()
{
ClientTickEvents.END_CLIENT_TICK.register(client -> {
ClientPlayerEntity player = client.player;
Entity camera = client.getCameraEntity();
ClientWorld world = client.world;
SoundManager manager = client.getSoundManager();
if (player == null || camera == null || world == null)
{
return;
}
HitResult hitResult = raycast(camera, client.options.getSimulationDistance().getValue() * 16);
if (hitResult == null || hitResult.getType() != HitResult.Type.ENTITY)
{
resetTarget(manager);
return;
}
EntityHitResult hit = (EntityHitResult)hitResult;
if (hit.getEntity() instanceof LivingEntity livingEntity)
{
if (TARGET != livingEntity)
{
resetTarget(manager);
client.getSoundManager().play(PositionedSoundInstance.record(SoundEvent.of(Identifier.of("oiia", "oiia")), livingEntity.getEntityPos()));
}
TARGET = livingEntity;
return;
}
resetTarget(manager);
});
}
}

View File

@@ -0,0 +1,8 @@
package com.kasetoatz.oiia.interfaces;
public interface ILivingEntity
{
float oiia$getRotation();
void oiia$setRotation(float value);
int oiia$getTick();
}

View File

@@ -0,0 +1,47 @@
package com.kasetoatz.oiia.mixin;
import com.kasetoatz.oiia.interfaces.ILivingEntity;
import net.minecraft.entity.LivingEntity;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import static com.kasetoatz.oiia.Oiia.TARGET;
@Mixin(LivingEntity.class)
public class LivingEntityMixin implements ILivingEntity
{
@Unique private float rotation = 0F;
@Unique private int tick = 0;
@Override
public float oiia$getRotation()
{
return rotation;
}
@Override
public void oiia$setRotation(float value)
{
rotation = value;
}
@Override
public int oiia$getTick()
{
return tick;
}
@Inject(method="tick", at=@At("HEAD"))
public void tick(CallbackInfo ci)
{
if ((Object)this == TARGET)
{
tick++;
return;
}
tick = 0;
}
}

View File

@@ -0,0 +1,33 @@
package com.kasetoatz.oiia.mixin;
import com.kasetoatz.oiia.interfaces.ILivingEntity;
import net.minecraft.client.render.entity.LivingEntityRenderer;
import net.minecraft.client.render.entity.state.LivingEntityRenderState;
import net.minecraft.entity.LivingEntity;
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 static com.kasetoatz.oiia.Oiia.TARGET;
@Mixin(LivingEntityRenderer.class)
public class LivingEntityRendererMixin
{
@Inject(method="updateRenderState(Lnet/minecraft/entity/LivingEntity;Lnet/minecraft/client/render/entity/state/LivingEntityRenderState;F)V", at=@At("TAIL"))
public void shouldFlipUpsideDown(LivingEntity entity, LivingEntityRenderState state, float f, CallbackInfo ci)
{
if (entity != TARGET)
{
return;
}
ILivingEntity accessor = (ILivingEntity)entity;
float rotation = accessor.oiia$getRotation();
int tick = accessor.oiia$getTick();
if ((tick <= 35 || tick >= 60) && tick <= 100)
{
state.bodyYaw = rotation;
accessor.oiia$setRotation(rotation + ((tick > 35) ? 1F : 2.5F));
}
}
}

View File

@@ -0,0 +1,5 @@
{
"oiia": {
"sounds": ["oiia:oiia"]
}
}

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,24 @@
{
"schemaVersion": 1,
"id": "oiia",
"version": "${version}",
"name": "OIIA",
"description": "",
"authors": [],
"contact": {},
"license": "MIT",
"environment": "client",
"entrypoints": {
"client": [
"com.kasetoatz.oiia.Oiia"
]
},
"mixins": [
"oiia.mixins.json"
],
"depends": {
"fabricloader": ">=${loader_version}",
"fabric": "*",
"minecraft": "${minecraft_version}"
}
}

View File

@@ -0,0 +1,16 @@
{
"required": true,
"minVersion": "0.8",
"package": "com.kasetoatz.oiia.mixin",
"compatibilityLevel": "JAVA_21",
"client": [
"LivingEntityRendererMixin",
"LivingEntityMixin"
],
"injectors": {
"defaultRequire": 1
},
"overwrites": {
"requireAnnotations": true
}
}