package com.mcmoddev.communitymod.gegy.squashable; import com.mcmoddev.communitymod.CommunityGlobals; import com.mcmoddev.communitymod.ISubMod; import com.mcmoddev.communitymod.SubMod; import net.minecraft.client.model.ModelBase; import net.minecraft.client.renderer.entity.RenderLivingBase; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.util.EnumFacing; import net.minecraft.util.ResourceLocation; import net.minecraftforge.client.event.RenderLivingEvent; import net.minecraftforge.common.capabilities.Capability; import net.minecraftforge.common.capabilities.CapabilityInject; import net.minecraftforge.common.capabilities.CapabilityManager; import net.minecraftforge.event.AttachCapabilitiesEvent; import net.minecraftforge.event.entity.living.LivingEvent; import net.minecraftforge.event.entity.player.PlayerEvent; import net.minecraftforge.fml.common.ObfuscationReflectionHelper; import net.minecraftforge.fml.common.event.FMLPreInitializationEvent; import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; import net.minecraftforge.fml.common.network.simpleimpl.SimpleNetworkWrapper; import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.fml.relauncher.SideOnly; import java.lang.reflect.Field; @SubMod( name = "squashable", description = "Flatten entities with a piston", attribution = "gegy1000" ) public class SquashableMod implements ISubMod { @CapabilityInject(Squashable.class) private static Capability<Squashable> squashableCapability; private static final SimpleNetworkWrapper NETWORK = new SimpleNetworkWrapper(CommunityGlobals.MOD_ID + ".squashable"); private static final Field PISTON_PUSH_TIME_FIELD = ObfuscationReflectionHelper.findField(Entity.class, "field_191506_aJ"); private static final Field PISTON_DELTAS_FIELD = ObfuscationReflectionHelper.findField(Entity.class, "field_191505_aI"); private static long getPistonPushTime(Entity entity) { try { return (long) PISTON_PUSH_TIME_FIELD.get(entity); } catch (IllegalAccessException e) { return 0; } } private static double[] getPistonDeltas(Entity entity) { try { return (double[]) PISTON_DELTAS_FIELD.get(entity); } catch (IllegalAccessException e) { return new double[3]; } } @Override public void onPreInit(FMLPreInitializationEvent event) { CapabilityManager.INSTANCE.register(Squashable.class, Squashable.Storage.INSTANCE, Squashable::new); NETWORK.registerMessage(SquashEntityMessage.Handler.class, SquashEntityMessage.class, 0, Side.CLIENT); } @SubscribeEvent public static void onAttachEntityCapabilities(AttachCapabilitiesEvent<Entity> event) { event.addCapability(new ResourceLocation(CommunityGlobals.MOD_ID, "squashable"), new Squashable()); } @SubscribeEvent public static void onLivingUpdate(LivingEvent.LivingUpdateEvent event) { EntityLivingBase entity = event.getEntityLiving(); if (entity.world.isRemote) { return; } if (entity.collidedHorizontally && isBeingPushedByPiston(entity)) { Squashable squashable = entity.getCapability(squashableCap(), null); if (squashable == null) { return; } double[] pistonDeltas = getPistonDeltas(entity); double pushedAngle = Math.atan2(pistonDeltas[2], pistonDeltas[0]); EnumFacing.Axis faceAxis = EnumFacing.fromAngle(entity.rotationYaw).getAxis(); EnumFacing.Axis pushAxis = EnumFacing.fromAngle(pushedAngle).getAxis(); EnumFacing.Axis squashAxis = faceAxis == pushAxis ? EnumFacing.Axis.Z : EnumFacing.Axis.X; squashable.squash(squashAxis); NETWORK.sendToAllTracking(new SquashEntityMessage(entity, squashAxis), entity); } } @SubscribeEvent public static void onEntityTrack(PlayerEvent.StartTracking event) { Entity target = event.getTarget(); Squashable squashable = target.getCapability(squashableCap(), null); if (squashable == null) { return; } EnumFacing.Axis squashedAxis = squashable.getSquashedAxis(); if (squashedAxis != null) { NETWORK.sendTo(new SquashEntityMessage(target, squashedAxis), (EntityPlayerMP) event.getEntityPlayer()); } } private static boolean isBeingPushedByPiston(Entity entity) { // we're always a tick late long pistonPushTime = getPistonPushTime(entity) + 1; return pistonPushTime == entity.world.getTotalWorldTime(); } public static Capability<Squashable> squashableCap() { if (squashableCapability == null) { throw new IllegalStateException("Squashable capability not initialize"); } return squashableCapability; } @SideOnly(Side.CLIENT) private static ModelBase originalModel; @SubscribeEvent @SideOnly(Side.CLIENT) public static void onRenderLivingPre(RenderLivingEvent.Pre<?> event) { EntityLivingBase entity = event.getEntity(); Squashable squashable = entity.getCapability(squashableCap(), null); if (squashable == null) { return; } EnumFacing.Axis squashedAxis = squashable.getSquashedAxis(); if (squashedAxis != null) { RenderLivingBase<?> renderer = event.getRenderer(); originalModel = renderer.getMainModel(); ModelHooks.setMainModel(renderer, new FlattenedModel(originalModel, squashedAxis)); } } @SubscribeEvent @SideOnly(Side.CLIENT) public static void onRenderLivingPost(RenderLivingEvent.Post<?> event) { if (originalModel != null) { ModelHooks.setMainModel(event.getRenderer(), originalModel); originalModel = null; } } @SideOnly(Side.CLIENT) private static class ModelHooks { private static final Field RENDERER_MODEL_FIELD = ObfuscationReflectionHelper.findField(RenderLivingBase.class, "field_77045_g"); private static void setMainModel(RenderLivingBase<?> renderer, ModelBase model) { try { RENDERER_MODEL_FIELD.set(renderer, model); } catch (IllegalAccessException e) { throw new RuntimeException(e); } } } }