<|start_filename|>src/main/java/dev/necauqua/mods/cm/recipes/BlueStarDecraftRecipe.java<|end_filename|>
/*
 * Copyright (c) 2017-2021 <NAME> <<EMAIL>>
 * Licensed under MIT, see the LICENSE file for details.
 */

package dev.necauqua.mods.cm.recipes;

import dev.necauqua.mods.cm.advancements.AdvancementTriggers;
import net.minecraft.inventory.InventoryCrafting;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.item.crafting.IRecipe;
import net.minecraft.item.crafting.Ingredient;
import net.minecraft.util.NonNullList;
import net.minecraft.world.World;
import net.minecraftforge.common.ForgeHooks;
import net.minecraftforge.event.RegistryEvent;
import net.minecraftforge.fml.common.Mod.EventBusSubscriber;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.common.registry.GameRegistry.ObjectHolder;
import net.minecraftforge.registries.IForgeRegistryEntry;

import javax.annotation.Nonnull;
import java.util.stream.IntStream;

import static dev.necauqua.mods.cm.ChiseledMe.MODID;
import static dev.necauqua.mods.cm.ChiseledMe.ns;
import static net.minecraft.init.Blocks.LAPIS_BLOCK;
import static net.minecraft.init.Items.NETHER_STAR;

@EventBusSubscriber(modid = MODID)
public final class BlueStarDecraftRecipe extends IForgeRegistryEntry.Impl<IRecipe> implements IRecipe {

    @ObjectHolder("chiseled_me:blue_star")
    private static Item BLUE_STAR;

    public BlueStarDecraftRecipe() {
        setRegistryName(ns("blue_star_decraft"));
    }

    @Override
    public boolean canFit(int width, int height) {
        return width * height >= 1;
    }

    @Override
    public boolean matches(@Nonnull InventoryCrafting inv, @Nonnull World world) {
        boolean once = false;
        for (int i = 0; i < inv.getSizeInventory(); i++) {
            ItemStack stack = inv.getStackInSlot(i);
            if (stack.getItem() == BLUE_STAR) {
                once = true;
            } else if (!stack.isEmpty()) {
                return false;
            }
        }
        return once;
    }

    @Nonnull
    @Override
    public ItemStack getCraftingResult(@Nonnull InventoryCrafting inv) {
        long count = IntStream.range(0, inv.getSizeInventory()).filter(i -> !inv.getStackInSlot(i).isEmpty()).count();
        return new ItemStack(LAPIS_BLOCK, (int) count);
    }

    @Nonnull
    @Override
    public ItemStack getRecipeOutput() {
        return new ItemStack(LAPIS_BLOCK);
    }

    @Override
    @Nonnull
    public NonNullList<ItemStack> getRemainingItems(@Nonnull InventoryCrafting inv) {
        NonNullList<ItemStack> ret = NonNullList.withSize(inv.getSizeInventory(), ItemStack.EMPTY);
        IntStream.range(0, inv.getSizeInventory())
                .filter(i -> inv.getStackInSlot(i).getItem() == BLUE_STAR)
                .forEach(i -> ret.set(i, new ItemStack(NETHER_STAR)));

        AdvancementTriggers.BLUE_STAR_DECRAFT.trigger(ForgeHooks.getCraftingPlayer());

        return ret;
    }

    @Override
    public NonNullList<Ingredient> getIngredients() {
        NonNullList<Ingredient> list = NonNullList.create();
        list.add(Ingredient.fromItem(BLUE_STAR));
        return list;
    }

    @SubscribeEvent
    public static void on(RegistryEvent.Register<IRecipe> e) {
        e.getRegistry().register(new BlueStarDecraftRecipe());
    }
}


<|start_filename|>src/main/java/dev/necauqua/mods/cm/mixin/entity/ai/EntityAITargetMixin.java<|end_filename|>
/*
 * Copyright (c) 2017-2021 <NAME> <<EMAIL>>
 * Licensed under MIT, see the LICENSE file for details.
 */

package dev.necauqua.mods.cm.mixin.entity.ai;

import dev.necauqua.mods.cm.api.ISized;
import net.minecraft.entity.EntityCreature;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.ai.EntityAITarget;
import net.minecraft.entity.ai.attributes.IAttributeInstance;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Constant;
import org.spongepowered.asm.mixin.injection.ModifyConstant;
import org.spongepowered.asm.mixin.injection.Redirect;

@Mixin(EntityAITarget.class)
public abstract class EntityAITargetMixin {

    @ModifyConstant(method = "getTargetDistance", constant = @Constant(doubleValue = 16.0))
    double getTargetDistance(double constant) {
        return constant * ((ISized) taskOwner).getSizeCM();
    }

    @Redirect(method = "getTargetDistance", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/ai/attributes/IAttributeInstance;getAttributeValue()D"))
    double getTargetDistance(IAttributeInstance self) {
        double size = ((ISized) taskOwner).getSizeCM();
        EntityLivingBase target = taskOwner.getAttackTarget();
        return self.getAttributeValue() * (target != null ? Math.sqrt(size * ((ISized) target).getSizeCM()) : size);
    }

    @Shadow
    @Final
    protected EntityCreature taskOwner;
}


<|start_filename|>src/main/java/dev/necauqua/mods/cm/mixin/entity/ai/EntityBlazeAIFireballAttackMixin.java<|end_filename|>
/*
 * Copyright (c) 2017-2021 <NAME> <<EMAIL>>
 * Licensed under MIT, see the LICENSE file for details.
 */

package dev.necauqua.mods.cm.mixin.entity.ai;

import dev.necauqua.mods.cm.api.ISized;
import dev.necauqua.mods.cm.api.IWorldPreciseEvents;
import net.minecraft.entity.ai.attributes.IAttributeInstance;
import net.minecraft.entity.monster.EntityBlaze;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Constant;
import org.spongepowered.asm.mixin.injection.ModifyConstant;
import org.spongepowered.asm.mixin.injection.Redirect;

@Mixin(targets = "net.minecraft.entity.monster.EntityBlaze$AIFireballAttack")
public final class EntityBlazeAIFireballAttackMixin {

    // fireball vertical offset
    @ModifyConstant(method = "updateTask()V", constant = @Constant(doubleValue = 0.5))
    double updateTask(double constant) {
        return constant * ((ISized) blaze).getSizeCM();
    }

    // melee attack, lol
    @ModifyConstant(method = "updateTask()V", constant = @Constant(doubleValue = 4.0))
    double updateTaskSq(double constant) {
        assert blaze.getAttackTarget() != null;
        return constant * ((ISized) blaze).getSizeCM() * ((ISized) blaze.getAttackTarget()).getSizeCM();
    }

    // sounds
    @Redirect(method = "updateTask()V", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/World;playEvent(Lnet/minecraft/entity/player/EntityPlayer;ILnet/minecraft/util/math/BlockPos;I)V"))
    void playEvent(World self, EntityPlayer player, int type, BlockPos pos, int data) {
        double size = ((ISized) blaze).getSizeCM();
        ((IWorldPreciseEvents) self).playEvent(null, type, pos, data, size, blaze.getPositionEyes(1.0f));
    }

    @ModifyConstant(method = "getFollowDistance()D", constant = @Constant(doubleValue = 16.0))
    double getFollowDistance(double constant) {
        return constant * ((ISized) blaze).getSizeCM();
    }

    @Redirect(method = "getFollowDistance()D", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/ai/attributes/IAttributeInstance;getAttributeValue()D"))
    double getFollowDistance(IAttributeInstance self) {
        return self.getAttributeValue() * ((ISized) blaze).getSizeCM();
    }

    @Shadow
    @Final
    private EntityBlaze blaze;
}


<|start_filename|>src/main/resources/assets/chiseled_me/recipes/pym_container.json<|end_filename|>
{
  "type": "minecraft:crafting_shaped",
  "pattern": [
    "idi",
    "dpd",
    "idi"
  ],
  "key": {
    "i": {
      "item": "minecraft:iron_ingot"
    },
    "d": {
      "item": "minecraft:diamond"
    },
    "p": {
      "type": "potion",
      "potion_type": "regular",
      "potion": "minecraft:awkward",
      "__comment": "yes, I added a full-blown ingredient factory because neither MC nor Forge has one (at the time of writing)",
      "__comment2": "didn't use item_nbt ingradient because it matches exact nbt (so e.g. renamed awkward potion wouldn't work), yay ocd"
    }
  },
  "result": {
    "item": "pym_container"
  }
}


<|start_filename|>src/main/java/dev/necauqua/mods/cm/size/IEntityExtras.java<|end_filename|>
/*
 * Copyright (c) 2017-2021 <NAME> <<EMAIL>>
 * Licensed under MIT, see the LICENSE file for details.
 */

package dev.necauqua.mods.cm.size;

public interface IEntityExtras {

    void setOriginalWidthCM(float width);

    void setOriginalHeightCM(float height);

    void onUpdateCM();
}


<|start_filename|>src/main/java/dev/necauqua/mods/cm/mixin/compat/RandomPatchesFixMixin.java<|end_filename|>
/*
 * Copyright (c) 2017-2021 <NAME> <<EMAIL>>
 * Licensed under MIT, see the LICENSE file for details.
 */

package dev.necauqua.mods.cm.mixin.compat;

import dev.necauqua.mods.cm.api.IRenderSized;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.EntityRenderer;
import net.minecraft.entity.Entity;
import org.spongepowered.asm.mixin.*;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Coerce;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;

@Pseudo
@Mixin(targets = "com.therandomlabs.randompatches.hook.client.EntityRendererHook", remap = false)
public final class RandomPatchesFixMixin {

    private static boolean $cm$wasResizing = false;

    // funny that this coerse+shadow combination allowed me to not compile-depend
    // on yet another 'fix-the-vanilla-mod-that-likely-has-no-maven' mod yey

    @Dynamic
    @Inject(method = "orientCamera", at = @At("HEAD"), cancellable = true)
    private static void orientCamera(float partialTicks, EntityRenderer renderer, @Coerce Object hook, CallbackInfo ci) {
        Entity entity = mc.getRenderViewEntity();
        assert entity != null;
        boolean isResizing = ((IRenderSized) entity).isResizingCM();
        if (isResizing) {
            $cm$wasResizing = true;
            ci.cancel();
        } else if ($cm$wasResizing) {
            $cm$wasResizing = false;
            ((RandomPatchesFixMixin) hook).lastEyeHeight = ((RandomPatchesFixMixin) hook).eyeHeight = entity.getEyeHeight();
        }
    }

    @Shadow
    @Final
    private static Minecraft mc;

    @Shadow
    private float lastEyeHeight;

    @Shadow
    private float eyeHeight;
}


<|start_filename|>src/main/java/dev/necauqua/mods/cm/api/IWorldPreciseSounds.java<|end_filename|>
/*
 * Copyright (c) 2017-2021 <NAME> <<EMAIL>>
 * Licensed under MIT, see the LICENSE file for details.
 */

package dev.necauqua.mods.cm.api;

import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.util.SoundCategory;
import net.minecraft.util.SoundEvent;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World;

import javax.annotation.Nullable;

/**
 * This interface is a part of the mixin that adds necessary modifications
 * to the vanilla sound system to allow precise world positioning
 * and 'resizing' of sounds.
 * <p>
 * It is implemented on a {@link World} class by the mixin.
 * <p>
 * The size is not part of the volume to avoid various volume clamping and is just used to fool
 * the sound system into thinking the sound is further/closer to the listener than it actually is,
 * based on the source size.
 */
public interface IWorldPreciseSounds {

    /**
     * Same as {@link World#playSound(double, double, double, SoundEvent, SoundCategory, float, float, boolean)}
     * but also carries the size of the sound source.
     * <p>
     * This one has a client-only effect, just like the vanilla method.
     */
    void playSound(Vec3d pos, SoundEvent soundIn, SoundCategory category, float volume, float pitch, boolean distanceDelay, double size);

    /**
     * Same as {@link World#playSound(EntityPlayer, double, double, double, SoundEvent, SoundCategory, float, float)}
     * but also carries the size of the sound source.
     * <p>
     * This one is the network-sent version, just like the vanilla method.
     */
    void playSound(@Nullable EntityPlayer player, Vec3d pos, SoundEvent soundIn, SoundCategory category, float volume, float pitch, double size);
}


<|start_filename|>src/main/resources/chiseled-me.hack.mixins.json<|end_filename|>
{
  "required": true,
  "package": "dev.necauqua.mods.cm.mixin",
  "refmap": "chiseled-me.mixins.refmap.json",
  "target": "@env(INIT)",
  "minVersion": "0.8",
  "compatibilityLevel": "JAVA_8",
  "mixins": ["compat.ClassloadingHackMixin"],
  "injectors": {
    "defaultRequire": 1
  }
}


<|start_filename|>src/main/java/dev/necauqua/mods/cm/mixin/entity/item/EntityFireworkRocketMixin.java<|end_filename|>
/*
 * Copyright (c) 2017-2021 <NAME> <<EMAIL>>
 * Licensed under MIT, see the LICENSE file for details.
 */

package dev.necauqua.mods.cm.mixin.entity.item;

import dev.necauqua.mods.cm.api.ISized;
import dev.necauqua.mods.cm.mixin.entity.EntityMixin;
import dev.necauqua.mods.cm.size.EntitySizeInteractions;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.item.EntityFireworkRocket;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Constant;
import org.spongepowered.asm.mixin.injection.ModifyArg;
import org.spongepowered.asm.mixin.injection.ModifyConstant;

@Mixin(EntityFireworkRocket.class)
public abstract class EntityFireworkRocketMixin extends EntityMixin {

    @ModifyConstant(method = "onUpdate", constant = @Constant(doubleValue = 0.3))
    double onUpdate(double constant) {
        return boostedEntity != null ?
                constant * ((ISized) boostedEntity).getSizeCM() :
                constant * $cm$size;
    }

    @ModifyArg(method = "onUpdate", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/World;spawnParticle(Lnet/minecraft/util/EnumParticleTypes;DDDDDD[I)V"))
    int[] onUpdate(int[] args) {
        return EntitySizeInteractions.appendSize(args,
                boostedEntity != null ?
                        ((ISized) boostedEntity).getSizeCM() :
                        $cm$size);
    }

    @Shadow
    private EntityLivingBase boostedEntity;
}


<|start_filename|>src/main/java/dev/necauqua/mods/cm/mixin/client/RenderGuardianMixin.java<|end_filename|>
/*
 * Copyright (c) 2017-2021 <NAME> <<EMAIL>>
 * Licensed under MIT, see the LICENSE file for details.
 */

package dev.necauqua.mods.cm.mixin.client;

import dev.necauqua.mods.cm.api.IRenderSized;
import net.minecraft.client.renderer.entity.RenderGuardian;
import net.minecraft.entity.monster.EntityGuardian;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.ModifyVariable;

@Mixin(RenderGuardian.class)
public final class RenderGuardianMixin {

    // undo the scaling of the guardian beam
    @ModifyVariable(method = "doRender", ordinal = 3, at = @At(value = "LOAD", ordinal = 1))
    double doRender(double d, EntityGuardian entity, double x, double y, double z, float entityYaw, float partialTicks) {
        return d / ((IRenderSized) entity).getSizeCM(partialTicks);
    }
}


<|start_filename|>src/main/java/dev/necauqua/mods/cm/mixin/entity/item/EntityXPOrbMixin.java<|end_filename|>
/*
 * Copyright (c) 2017-2021 <NAME> <<EMAIL>>
 * Licensed under MIT, see the LICENSE file for details.
 */

package dev.necauqua.mods.cm.mixin.entity.item;

import dev.necauqua.mods.cm.mixin.entity.EntityMixin;
import net.minecraft.entity.item.EntityXPOrb;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.Constant;
import org.spongepowered.asm.mixin.injection.ModifyConstant;

@Mixin(EntityXPOrb.class)
public abstract class EntityXPOrbMixin extends EntityMixin {

    @ModifyConstant(method = "onUpdate", constant = @Constant(doubleValue = 8.0))
    double onUpdate(double constant) {
        return constant * $cm$size;
    }

    @ModifyConstant(method = "onUpdate", constant = @Constant(doubleValue = 64.0))
    double onUpdateSq(double constant) {
        return constant * $cm$size * $cm$size;
    }
}


<|start_filename|>src/main/java/dev/necauqua/mods/cm/mixin/WorldServerMixin.java<|end_filename|>
/*
 * Copyright (c) 2017-2021 <NAME> <<EMAIL>>
 * Licensed under MIT, see the LICENSE file for details.
 */

package dev.necauqua.mods.cm.mixin;

import dev.necauqua.mods.cm.api.IWorldPreciseEvents;
import dev.necauqua.mods.cm.api.IWorldPreciseSounds;
import net.minecraft.crash.CrashReport;
import net.minecraft.crash.CrashReportCategory;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.util.ReportedException;
import net.minecraft.util.SoundCategory;
import net.minecraft.util.SoundEvent;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.IWorldEventListener;
import net.minecraft.world.WorldServer;
import net.minecraftforge.event.ForgeEventFactory;
import net.minecraftforge.event.entity.PlaySoundAtEntityEvent;
import org.spongepowered.asm.mixin.Mixin;

import javax.annotation.Nullable;

// just copying the WorldMixin interface implementation here
// because apparently it fails on some servers for absolutely unknown reasons
@Mixin(WorldServer.class)
public abstract class WorldServerMixin extends WorldMixin implements IWorldPreciseEvents, IWorldPreciseSounds {

    @SuppressWarnings("ConstantConditions") // that null player lol
    @Override
    public void playEvent(EntityPlayer player, int type, BlockPos blockPos, int data, double size, Vec3d pos) {
        // same as playEvent(null, type, pos, data), just rerouting to the method with extra args
        try {
            for (IWorldEventListener listener : eventListeners) {
                if (listener instanceof IWorldPreciseEvents) {
                    ((IWorldPreciseEvents) listener).playEvent(null, type, blockPos, data, size, pos);
                }
            }
        } catch (Throwable e) {
            CrashReport report = CrashReport.makeCrashReport(e, "Playing level event");
            CrashReportCategory crashreportcategory3 = report.makeCategory("Level event being played");
            crashreportcategory3.addCrashSection("Block coordinates", CrashReportCategory.getCoordinateInfo(blockPos));
            crashreportcategory3.addCrashSection("Event source", null);
            crashreportcategory3.addCrashSection("Event type", type);
            crashreportcategory3.addCrashSection("Event data", data);
            throw new ReportedException(report);
        }
    }

    @Override
    public void playSound(Vec3d pos, SoundEvent soundIn, SoundCategory category, float volume, float pitch, boolean distanceDelay, double size) {}

    @Override
    public void playSound(@Nullable EntityPlayer player, Vec3d pos, SoundEvent soundIn, SoundCategory category, float volume, float pitch, double size) {
        PlaySoundAtEntityEvent event = ForgeEventFactory.onPlaySoundAtEntity(player, soundIn, category, volume, pitch);
        if (event.isCanceled() || event.getSound() == null) {
            return;
        }
        soundIn = event.getSound();
        category = event.getCategory();
        volume = event.getVolume();
        pitch = event.getPitch();

        for (IWorldEventListener listener : eventListeners) {
            if (listener instanceof IWorldPreciseEvents) {
                ((IWorldPreciseSounds) listener).playSound(player, pos, soundIn, category, volume, pitch, size);
            }
        }
    }
}


<|start_filename|>src/main/java/dev/necauqua/mods/cm/mixin/client/ItemRendererMixin.java<|end_filename|>
/*
 * Copyright (c) 2017-2021 <NAME> <<EMAIL>>
 * Licensed under MIT, see the LICENSE file for details.
 */

package dev.necauqua.mods.cm.mixin.client;

import dev.necauqua.mods.cm.api.IRenderSized;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.ItemRenderer;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.Constant;
import org.spongepowered.asm.mixin.injection.ModifyConstant;

@Mixin(ItemRenderer.class)
public final class ItemRendererMixin {

    // vertical collision for rendering the being inside of a block overlay
    @ModifyConstant(method = "renderOverlays", constant = @Constant(floatValue = 0.1f))
    float renderOverlays(float constant) {
        return (float) (constant * ((IRenderSized) mc.player).getSizeCM());
    }

    @Shadow
    @Final
    private Minecraft mc;
}


<|start_filename|>src/main/java/dev/necauqua/mods/cm/mixin/item/ItemEnderEyeMixin.java<|end_filename|>
/*
 * Copyright (c) 2017-2021 <NAME> <<EMAIL>>
 * Licensed under MIT, see the LICENSE file for details.
 */

package dev.necauqua.mods.cm.mixin.item;

import dev.necauqua.mods.cm.api.ISized;
import dev.necauqua.mods.cm.api.IWorldPreciseEvents;
import dev.necauqua.mods.cm.api.IWorldPreciseSounds;
import net.minecraft.entity.Entity;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.ItemEnderEye;
import net.minecraft.util.SoundCategory;
import net.minecraft.util.SoundEvent;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;

@Mixin(ItemEnderEye.class)
public final class ItemEnderEyeMixin {

    @Redirect(method = "onItemRightClick", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/World;spawnEntity(Lnet/minecraft/entity/Entity;)Z"))
    boolean onItemRightClick(World self, Entity entity, World world, EntityPlayer player) {
        ((ISized) entity).setSizeCM(((ISized) player).getSizeCM());
        return self.spawnEntity(entity);
    }

    @Redirect(method = "onItemRightClick", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/World;playEvent(Lnet/minecraft/entity/player/EntityPlayer;ILnet/minecraft/util/math/BlockPos;I)V"))
    void playEvent(World self, EntityPlayer player, int type, BlockPos pos, int data, World world, EntityPlayer player2) {
        double size = ((ISized) player2).getSizeCM();
        ((IWorldPreciseEvents) self).playEvent(player, type, pos, data, size, player2.getPositionEyes(1.0f));
    }

    @Redirect(method = "onItemRightClick", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/World;playSound(Lnet/minecraft/entity/player/EntityPlayer;DDDLnet/minecraft/util/SoundEvent;Lnet/minecraft/util/SoundCategory;FF)V"))
    void playSound(World self, EntityPlayer player, double x, double y, double z, SoundEvent sound, SoundCategory category, float volume, float pitch, World world, EntityPlayer player2) {
        double size = ((ISized) player2).getSizeCM();
        ((IWorldPreciseSounds) self).playSound(player, player2.getPositionEyes(1.0f), sound, category, volume, pitch, size);
    }
}


<|start_filename|>src/main/java/dev/necauqua/mods/cm/item/ItemRecalibrator.java<|end_filename|>
/*
 * Copyright (c) 2017-2021 <NAME> <<EMAIL>>
 * Licensed under MIT, see the LICENSE file for details.
 */
package dev.necauqua.mods.cm.item;

import dev.necauqua.mods.cm.Config;
import dev.necauqua.mods.cm.SidedHandler;
import dev.necauqua.mods.cm.advancements.AdvancementTriggers;
import dev.necauqua.mods.cm.advancements.SizeTrigger;
import dev.necauqua.mods.cm.api.IRenderSized;
import net.minecraft.block.BlockDispenser;
import net.minecraft.client.util.ITooltipFlag;
import net.minecraft.creativetab.CreativeTabs;
import net.minecraft.entity.Entity;
import net.minecraft.entity.item.EntityItem;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.item.EnumRarity;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.util.ActionResult;
import net.minecraft.util.EnumHand;
import net.minecraft.util.NonNullList;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.RayTraceResult;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World;
import net.minecraftforge.fml.common.Loader;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.List;

import static dev.necauqua.mods.cm.ChiseledMe.ns;
import static dev.necauqua.mods.cm.item.ItemRecalibrator.RecalibrationType.*;
import static dev.necauqua.mods.cm.size.ChangingSizeProcess.log2LerpTime;
import static net.minecraft.entity.player.EntityPlayer.REACH_DISTANCE;
import static net.minecraft.util.EnumActionResult.SUCCESS;

public final class ItemRecalibrator extends ItemMod {

    private static boolean entityItemBBoxOffset = true;

    public ItemRecalibrator() {
        super("recalibrator");
        setMaxStackSize(1);

        entityItemBBoxOffset = !Loader.isModLoaded("itemphysic");

        addPropertyOverride(ns("recalibrator_type"),
                (stack, worldIn, entityIn) -> {
                    int nbtFactor = getEffectFromStack(stack).type.getFactor();
                    if (nbtFactor != 0) {
                        return nbtFactor;
                    }
                    // this is so stupid, need that for advancement icons
                    int meta = stack.getMetadata();
                    return meta == 0 ? 0 : meta <= 12 ? -1 : 1;
                });

        // maybe someone someday will make some cool model/texture based on that
        addPropertyOverride(ns("recalibrator_tier"),
                (stack, worldIn, entityIn) -> {
                    int nbtTier = getEffectFromStack(stack).tier;
                    if (nbtTier != 0) {
                        return nbtTier;
                    }
                    // advancements should do it correctly here too
                    int meta = stack.getMetadata();
                    return meta == 0 ? 0 : meta <= 12 ? meta : meta - 12;
                });
        BlockDispenser.DISPENSE_BEHAVIOR_REGISTRY.putObject(this, (source, stack) -> {
            BlockPos at = source.getBlockPos().offset(source.getBlockState().getValue(BlockDispenser.FACING));
            List<Entity> list = source.getWorld().getEntitiesWithinAABB(Entity.class, new AxisAlignedBB(at));
            if (list.isEmpty()) {
                return stack;
            }
            RecalibrationEffect effect = ItemRecalibrator.getEffectFromStack(stack);
            ItemStack worked = stack.copy();
            for (Entity entity : list) {
                worked = effect.apply(entity, worked);
            }
            return worked;
        });
    }

    public static RecalibrationEffect getEffectFromStack(ItemStack stack) {
        return RecalibrationEffect.fromNBT(stack.getTagCompound());
    }

    public ItemStack create(RecalibrationType type, byte tier) {
        ItemStack stack = new ItemStack(this);
        NBTTagCompound nbt = new NBTTagCompound();
        nbt.setByte("type", (byte) type.getFactor());
        nbt.setByte("tier", tier);
        nbt.setInteger("charges", 0);
        stack.setTagCompound(nbt);
        return stack;
    }

    @Override
    @Nonnull
    public ActionResult<ItemStack> onItemRightClick(World world, EntityPlayer player, @Nonnull EnumHand hand) {
        ItemStack stack = player.getHeldItem(hand);

        if (!Config.allowRecalibratingOtherEntities || !player.isSneaking()) {
            ItemStack used = getEffectFromStack(stack).apply(player, stack.copy());
            return new ActionResult<>(SUCCESS, player.isCreative() ? stack : used);
        }

        double reach = player.getEntityAttribute(REACH_DISTANCE).getAttributeValue();
        Vec3d start = player.getPositionEyes(1.0f);
        Vec3d dir = player.getLook(1.0f).scale(reach);
        Vec3d end = start.add(dir);

        Entity match = null;
        double maxDistSq = reach * reach; // for square comparison

        for (Entity entity : world.getEntitiesWithinAABBExcludingEntity(player, new AxisAlignedBB(start.x, start.y, start.z, end.x, end.y, end.z))) {
            if (entity.isInvisible()) {
                continue;
            }
            AxisAlignedBB aabb = entity.getEntityBoundingBox();
            if (entityItemBBoxOffset && entity instanceof EntityItem) {
                double h = aabb.maxY - aabb.minY;
                aabb = new AxisAlignedBB(aabb.minX, aabb.minY + h, aabb.minZ, aabb.maxX, aabb.maxY + h, aabb.maxZ);
            }
            RayTraceResult result = aabb.calculateIntercept(start, end);
            if (result == null) {
                continue;
            }
            double distSq = start.squareDistanceTo(result.hitVec);
            if (distSq < maxDistSq) {
                maxDistSq = distSq;
                match = entity;
            }
        }
        if (match != null && (Config.allowRecalibratingOtherPlayers || !(match instanceof EntityPlayer))) {
            ItemStack used = getEffectFromStack(stack).apply(match, stack.copy());
            return new ActionResult<>(SUCCESS, player.isCreative() ? stack : used);
        }
        return new ActionResult<>(SUCCESS, stack);
    }

    @Override
    @Nonnull
    public String getItemStackDisplayName(@Nonnull ItemStack stack) {
        return getEffectFromStack(stack).getDisplayString("name");
    }

    @Override
    public void addInformation(ItemStack stack, @Nullable World worldIn, List<String> tooltip, ITooltipFlag flagIn) {
        RecalibrationEffect effect = getEffectFromStack(stack);
        tooltip.add(effect.getDisplayString("tooltip"));
        String uses = effect.getChargesLeft();
        if (uses != null) {
            tooltip.add(uses);
        }
    }

    @SuppressWarnings("deprecation") // dont want to hard-depend on new forge (yet)
    @Override
    public EnumRarity getRarity(ItemStack stack) {
        RecalibrationEffect effect = getEffectFromStack(stack);
        return effect.type == RESET ?
                EnumRarity.UNCOMMON :
                effect.tier <= (effect.type == REDUCTION ? 8 : 2) ?
                        EnumRarity.RARE :
                        EnumRarity.EPIC;
    }

    @Override
    public boolean showDurabilityBar(ItemStack stack) {
        return getEffectFromStack(stack).showBar();
    }

    @Override
    public double getDurabilityForDisplay(ItemStack stack) {
        return getEffectFromStack(stack).getBar();
    }

    @Override
    public void getSubItems(CreativeTabs tab, NonNullList<ItemStack> items) {
        super.getSubItems(tab, items); // reset one
        if (isInCreativeTab(tab)) {
            for (byte i = 1; i <= 12; i++) {
                items.add(create(REDUCTION, i));
            }
            for (byte i = 1; i <= 4; i++) {
                items.add(create(AMPLIFICATION, i));
            }
        }
    }

    public enum RecalibrationType {
        REDUCTION(-1, 12),
        RESET(0, 0),
        AMPLIFICATION(1, 4);

        private final int factor;
        private final int maxTier;

        RecalibrationType(int factor, int maxTier) {
            this.factor = factor;
            this.maxTier = maxTier;
        }

        public int getFactor() {
            return factor;
        }

        public int getMaxTier() {
            return maxTier;
        }

        static RecalibrationType fromFactor(int factor) {
            switch (factor) {
                case -1:
                    return REDUCTION;
                case 1:
                    return AMPLIFICATION;
                default:
                    return RESET;
            }
        }
    }

    public static class RecalibrationEffect {

        private final RecalibrationType type;
        private final int tier;
        private final int charges;
        private final float size;
        private final float maxCharges;

        public static RecalibrationEffect fromNBT(@Nullable NBTTagCompound nbt) {
            if (nbt != null) {
                RecalibrationType type = fromFactor(nbt.getByte("type"));
                if (type != RESET) {
                    byte tier = nbt.getByte("tier");
                    if (tier > 0 && tier <= type.getMaxTier()) {
                        int charges = nbt.getInteger("charges");
                        if (charges >= 0) {
                            return new RecalibrationEffect(type, tier, charges);
                        }
                    }
                }
            }
            return new RecalibrationEffect(RESET, (byte) 0, 0);
        }

        private RecalibrationEffect(RecalibrationType type, int tier, int charges) {
            this.type = type;
            this.tier = tier;
            this.charges = charges;
            size = (float) Math.pow(2.0, tier * type.getFactor());
            maxCharges = (float) Math.pow(2.0, type.getMaxTier() - tier + 4);
        }

        public boolean showBar() {
            return type != RESET && charges > 0;
        }

        public double getBar() {
            return charges / maxCharges;
        }

        public String getChargesLeft() {
            if (type == RESET) {
                return null;
            }
            return SidedHandler.instance.getLocalization("item.chiseled_me:recalibrator.charges", (int) (maxCharges - charges));
        }

        public String getDisplayString(String sub) {
            int s = (int) (type == REDUCTION ? 1.0f / size : size);
            String name = type.name().toLowerCase();
            return SidedHandler.instance.getLocalization("item.chiseled_me:recalibrator." + name + "." + sub, s);
        }

        public ItemStack apply(Entity entity, ItemStack stack) {
            boolean isPlayer = entity instanceof EntityPlayer;
            int i = isPlayer ? 1 : 2;
            IRenderSized sized = (IRenderSized) entity;
            double currentSize = sized.getSizeCM();
            if (size != currentSize) {
                if (!entity.world.isRemote) {
                    sized.setSizeCM(size, log2LerpTime(currentSize, size));
                }
                if (isPlayer) {
                    SizeTrigger.INSTANCE.trigger((EntityPlayer) entity, currentSize, size);
                } else {
                    i *= 4;
                }
            }
            if (type == RESET) {
                return stack;
            }
            if (charges < maxCharges - i) {
                NBTTagCompound nbt = stack.getTagCompound();
                if (nbt == null) {
                    nbt = new NBTTagCompound();
                }
                nbt.setInteger("charges", charges + i);
                stack.setTagCompound(nbt);
            } else {
                stack.setTagCompound(null); // set recalibrator to reset mode
                if (entity instanceof EntityPlayerMP) {
                    EntityPlayerMP player = (EntityPlayerMP) entity;
                    if (!player.isCreative()) { // because in creative the item wont be replaced
                        AdvancementTriggers.RECALIBRATOR_RESET.trigger(player);
                    }
                }
            }
            return stack;
        }
    }
}


<|start_filename|>src/main/java/dev/necauqua/mods/cm/mixin/WorldMixin.java<|end_filename|>
/*
 * Copyright (c) 2017-2021 <NAME> <<EMAIL>>
 * Licensed under MIT, see the LICENSE file for details.
 */

package dev.necauqua.mods.cm.mixin;

import dev.necauqua.mods.cm.api.IWorldPreciseEvents;
import dev.necauqua.mods.cm.api.IWorldPreciseSounds;
import dev.necauqua.mods.cm.size.IEntityExtras;
import net.minecraft.crash.CrashReport;
import net.minecraft.crash.CrashReportCategory;
import net.minecraft.entity.Entity;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.ReportedException;
import net.minecraft.util.SoundCategory;
import net.minecraft.util.SoundEvent;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.RayTraceResult;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.IWorldEventListener;
import net.minecraft.world.World;
import net.minecraftforge.event.ForgeEventFactory;
import net.minecraftforge.event.entity.PlaySoundAtEntityEvent;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.ModifyVariable;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;

import javax.annotation.Nullable;
import java.util.List;

@Mixin(World.class)
public abstract class WorldMixin implements IWorldPreciseEvents, IWorldPreciseSounds {

    // replace the initial value of `raytraceresult2` so it is not null
    // so the game would stop spamming `Null returned as 'hitResult', this shouldn't happen!` errors
    @ModifyVariable(
            method = "rayTraceBlocks(Lnet/minecraft/util/math/Vec3d;Lnet/minecraft/util/math/Vec3d;ZZZ)Lnet/minecraft/util/math/RayTraceResult;",
            at = @At(value = "STORE", ordinal = 1))
    RayTraceResult rayTraceBlocks(RayTraceResult result, Vec3d vec31, Vec3d vec32, boolean stopOnLiquid, boolean ignoreBlockWithoutBoundingBox, boolean returnLastUncollidableBlock) {
        int x = MathHelper.floor(vec31.x);
        int y = MathHelper.floor(vec31.y);
        int z = MathHelper.floor(vec31.z);
        return new RayTraceResult(RayTraceResult.Type.MISS, vec32, EnumFacing.DOWN, new BlockPos(x, y, z));
    }

    @SuppressWarnings("ConstantConditions") // that null player lol
    @Override
    public void playEvent(EntityPlayer player, int type, BlockPos blockPos, int data, double size, Vec3d pos) {
        // same as playEvent(null, type, pos, data), just rerouting to the method with extra args
        try {
            for (IWorldEventListener listener : eventListeners) {
                if (listener instanceof IWorldPreciseEvents) {
                    ((IWorldPreciseEvents) listener).playEvent(null, type, blockPos, data, size, pos);
                }
            }
        } catch (Throwable e) {
            CrashReport report = CrashReport.makeCrashReport(e, "Playing level event");
            CrashReportCategory crashreportcategory3 = report.makeCategory("Level event being played");
            crashreportcategory3.addCrashSection("Block coordinates", CrashReportCategory.getCoordinateInfo(blockPos));
            crashreportcategory3.addCrashSection("Event source", null);
            crashreportcategory3.addCrashSection("Event type", type);
            crashreportcategory3.addCrashSection("Event data", data);
            throw new ReportedException(report);
        }
    }

    @Override
    public void playSound(Vec3d pos, SoundEvent soundIn, SoundCategory category, float volume, float pitch, boolean distanceDelay, double size) {}

    @Override
    public void playSound(@Nullable EntityPlayer player, Vec3d pos, SoundEvent soundIn, SoundCategory category, float volume, float pitch, double size) {
        PlaySoundAtEntityEvent event = ForgeEventFactory.onPlaySoundAtEntity(player, soundIn, category, volume, pitch);
        if (event.isCanceled() || event.getSound() == null) {
            return;
        }
        soundIn = event.getSound();
        category = event.getCategory();
        volume = event.getVolume();
        pitch = event.getPitch();

        for (IWorldEventListener listener : eventListeners) {
            if (listener instanceof IWorldPreciseEvents) {
                ((IWorldPreciseSounds) listener).playSound(player, pos, soundIn, category, volume, pitch, size);
            }
        }
    }

    // to not depend of whether entity calls super in it's onUpdate
    @Inject(method = "updateEntityWithOptionalForce", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/Entity;onUpdate()V"))
    void updateEntityWithOptionalForce(Entity entity, boolean forceUpdate, CallbackInfo ci) {
        ((IEntityExtras) entity).onUpdateCM();
    }

    @Shadow
    protected List<IWorldEventListener> eventListeners;
}


<|start_filename|>src/main/java/dev/necauqua/mods/cm/advancements/SizeTrigger.java<|end_filename|>
/*
 * Copyright (c) 2017-2021 <NAME> <<EMAIL>>
 * Licensed under MIT, see the LICENSE file for details.
 */

package dev.necauqua.mods.cm.advancements;

import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonObject;
import com.google.gson.JsonSyntaxException;
import dev.necauqua.mods.cm.ChiseledMe.Init;
import net.minecraft.advancements.CriteriaTriggers;
import net.minecraft.advancements.ICriterionInstance;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.util.JsonUtils;
import net.minecraft.util.ResourceLocation;

import java.util.HashMap;
import java.util.Map;

import static dev.necauqua.mods.cm.ChiseledMe.ns;

public final class SizeTrigger extends AdvancementTrigger<SizeTrigger.Instance> {

    private static final ResourceLocation ID = ns("size");
    private static final Map<String, SizeMatcher> MATCHERS = new HashMap<>();

    public static final SizeTrigger INSTANCE = new SizeTrigger();

    static {
        MATCHERS.put("exact", ((fromSize, toSize, condition) -> toSize == condition));
        MATCHERS.put("crossing", ((fromSize, toSize, condition) -> fromSize < toSize ?
                fromSize <= condition && condition <= toSize :
                toSize <= condition && condition <= fromSize));
        MATCHERS.put("lt", ((fromSize, toSize, condition) -> toSize < condition));
        MATCHERS.put("gt", ((fromSize, toSize, condition) -> toSize > condition));
        MATCHERS.put("le", ((fromSize, toSize, condition) -> toSize <= condition));
        MATCHERS.put("ge", ((fromSize, toSize, condition) -> toSize >= condition));
    }

    @Init
    private static void init() {
        CriteriaTriggers.register(INSTANCE);
    }

    private SizeTrigger() {
        super(ID);
    }

    @Override
    public Instance deserializeInstance(JsonObject json, JsonDeserializationContext context) {
        float size = JsonUtils.getFloat(json, "size");
        String matchStr = JsonUtils.getString(json, "match", "exact");
        SizeMatcher matcher = MATCHERS.get(matchStr);
        if (matcher == null) {
            throw new JsonSyntaxException("Expected match to be one of " + String.join(", ", MATCHERS.keySet()) + ", was " + matchStr);
        }
        return new Instance(size, matcher);
    }

    public void trigger(EntityPlayer player, double fromSize, double toSize) {
        trigger(player, instance -> instance.matches(fromSize, toSize));
    }

    @FunctionalInterface
    public interface SizeMatcher {

        boolean match(double fromSize, double toSize, double condition);
    }

    public static final class Instance implements ICriterionInstance {
        private final float size;
        private final SizeMatcher matcher;

        public Instance(float size, SizeMatcher matcher) {
            this.size = size;
            this.matcher = matcher;
        }

        public boolean matches(double fromSize, double toSize) {
            return matcher.match(fromSize, toSize, size);
        }

        @Override
        public ResourceLocation getId() {
            return ID;
        }
    }
}


<|start_filename|>src/main/java/dev/necauqua/mods/cm/mixin/entity/EntityAreaEffectCloudMixin.java<|end_filename|>
/*
 * Copyright (c) 2017-2021 <NAME> <<EMAIL>>
 * Licensed under MIT, see the LICENSE file for details.
 */

package dev.necauqua.mods.cm.mixin.entity;

import dev.necauqua.mods.cm.size.EntitySizeInteractions;
import net.minecraft.entity.EntityAreaEffectCloud;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.*;

@Mixin(EntityAreaEffectCloud.class)
public abstract class EntityAreaEffectCloudMixin extends EntityMixin {

    @ModifyVariable(method = "onUpdate", ordinal = 3, at = @At("STORE"))
    float onUpdate(float constant) {
        return (float) (constant * $cm$size);
    }

    @ModifyConstant(method = "onUpdate", constant = @Constant(floatValue = 0.2f))
    float onUpdateConstant(float constant) {
        return (float) (constant * $cm$size);
    }

    @ModifyArg(method = "onUpdate", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/World;spawnAlwaysVisibleParticle(IDDDDDD[I)V"))
    int[] onUpdate(int[] args) {
        return EntitySizeInteractions.appendSize(args, $cm$size);
    }
}


<|start_filename|>src/main/resources/assets/chiseled_me/advancements/antipolarization.json<|end_filename|>
{
  "parent": "chiseled_me:blue_nether_star",
  "display": {
    "icon": {
      "item": "chiseled_me:pym_essence_b"
    },
    "title": {
      "translate": "advancement.chiseled_me:antipolarization"
    },
    "description": {
      "translate": "advancement.chiseled_me:antipolarization.desc"
    },
    "announce_to_chat": false
  },
  "criteria": {
    "stuff": {
      "trigger": "minecraft:inventory_changed",
      "conditions": {
        "items": [
          {
            "item": "chiseled_me:pym_essence_b"
          }
        ]
      }
    }
  },
  "rewards": {
    "recipes": [
      "chiseled_me:recalibrator_amplification_1",
      "chiseled_me:recalibrator_amplification_2",
      "chiseled_me:recalibrator_amplification_3",
      "chiseled_me:recalibrator_amplification_4"
    ]
  }
}


<|start_filename|>src/main/java/dev/necauqua/mods/cm/mixin/client/RenderLivingMixin.java<|end_filename|>
/*
 * Copyright (c) 2017-2021 <NAME> <<EMAIL>>
 * Licensed under MIT, see the LICENSE file for details.
 */

package dev.necauqua.mods.cm.mixin.client;

import dev.necauqua.mods.cm.api.IRenderSized;
import net.minecraft.client.renderer.entity.RenderLiving;
import net.minecraft.entity.EntityLiving;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Constant;
import org.spongepowered.asm.mixin.injection.ModifyConstant;
import org.spongepowered.asm.mixin.injection.ModifyVariable;

@Mixin(RenderLiving.class)
public abstract class RenderLivingMixin {

    private double $cm$self, $cm$coarseSelf, $cm$holder;

    // undo the scaling of the leash

    @ModifyConstant(method = "renderLeash", constant = @Constant(doubleValue = 1.6))
    double renderLeashHeight(double constant, EntityLiving entityLiving, double x, double y, double z, float entityYaw, float partialTicks) {
        $cm$coarseSelf = ((IRenderSized) entityLiving).getSizeCM();
        $cm$self = ((IRenderSized) entityLiving).getSizeCM(partialTicks);
        $cm$holder = ((IRenderSized) entityLiving.getLeashHolder()).getSizeCM(partialTicks);

        return constant - entityLiving.height + entityLiving.height / $cm$coarseSelf * $cm$self;
    }

    @ModifyVariable(method = "renderLeash", ordinal = 14, at = @At("STORE"))
    double renderLeashHeightFix(double variable, EntityLiving entityLiving) {
        return variable - entityLiving.height / $cm$coarseSelf * $cm$self;
    }

    @ModifyConstant(method = "renderLeash", constant = @Constant(doubleValue = 0.4))
    double renderLeashWidth(double constant, EntityLiving entityLiving) {
        return constant * $cm$self / $cm$coarseSelf;
    }

    @ModifyVariable(method = "renderLeash", ordinal = 16, at = @At("STORE"))
    double renderLeashX(double variable) {
        return variable / $cm$self;
    }

    @ModifyVariable(method = "renderLeash", ordinal = 17, at = @At("STORE"))
    double renderLeashY(double variable) {
        return variable / $cm$self;
    }

    @ModifyVariable(method = "renderLeash", ordinal = 18, at = @At("STORE"))
    double renderLeashZ(double variable) {
        return variable / $cm$self;
    }

    @ModifyConstant(method = "renderLeash", constant = @Constant(doubleValue = 0.025))
    double renderLeashScale(double constant) {
        return constant / $cm$self * $cm$holder;
    }

    @ModifyConstant(method = "renderLeash", constant = {
            @Constant(doubleValue = 0.25),
            @Constant(doubleValue = 0.7, ordinal = 0),
            @Constant(doubleValue = 0.7, ordinal = 3), // except second and third 0.7

            @Constant(doubleValue = 0.5, ordinal = 1), // except first 0.5
            @Constant(doubleValue = 0.5, ordinal = 2),
            @Constant(doubleValue = 0.5, ordinal = 3),
    })
    double renderLeashHolderOffsets(double constant) {
        return constant * $cm$holder;
    }
}


<|start_filename|>src/main/java/dev/necauqua/mods/cm/mixin/entity/player/EntityOtherPlayerMPMixin.java<|end_filename|>
/*
 * Copyright (c) 2017-2021 <NAME> <<EMAIL>>
 * Licensed under MIT, see the LICENSE file for details.
 */

package dev.necauqua.mods.cm.mixin.entity.player;

import dev.necauqua.mods.cm.mixin.entity.EntityMixin;
import net.minecraft.client.entity.EntityOtherPlayerMP;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.Constant;
import org.spongepowered.asm.mixin.injection.ModifyConstant;

@Mixin(EntityOtherPlayerMP.class)
public abstract class EntityOtherPlayerMPMixin extends EntityMixin {

    // another limb swing animation, meh
    @ModifyConstant(method = "onUpdate", constant = @Constant(floatValue = 4.0f))
    float onUpdate(float constant) {
        return (float) (constant / $cm$size);
    }

    // that getPosition fix
    @ModifyConstant(method = "getPosition", constant = @Constant(doubleValue = 0.5))
    double getPosition(double constant) {
        return constant * $cm$size;
    }
}


<|start_filename|>src/main/java/dev/necauqua/mods/cm/mixin/entity/monster/EntityZombieVillagerMixin.java<|end_filename|>
package dev.necauqua.mods.cm.mixin.entity.monster;

import dev.necauqua.mods.cm.api.IWorldPreciseEvents;
import dev.necauqua.mods.cm.mixin.entity.EntityMixin;
import net.minecraft.entity.monster.EntityZombieVillager;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;

@Mixin(EntityZombieVillager.class)
public abstract class EntityZombieVillagerMixin extends EntityMixin {

    @Redirect(method = "finishConversion", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/World;playEvent(Lnet/minecraft/entity/player/EntityPlayer;ILnet/minecraft/util/math/BlockPos;I)V"))
    void playEvent(World self, EntityPlayer player, int type, BlockPos pos, int data) {
        ((IWorldPreciseEvents) self).playEvent(null, type, pos, data, $cm$size, getPositionEyes(1.0f));
    }
}


<|start_filename|>src/main/java/dev/necauqua/mods/cm/mixin/client/RenderGlobalMixin.java<|end_filename|>
/*
 * Copyright (c) 2017-2021 <NAME> <<EMAIL>>
 * Licensed under MIT, see the LICENSE file for details.
 */

package dev.necauqua.mods.cm.mixin.client;

import dev.necauqua.mods.cm.api.IWorldPreciseEvents;
import dev.necauqua.mods.cm.api.IWorldPreciseSounds;
import net.minecraft.client.multiplayer.WorldClient;
import net.minecraft.client.particle.Particle;
import net.minecraft.client.renderer.RenderGlobal;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.util.EnumParticleTypes;
import net.minecraft.util.SoundCategory;
import net.minecraft.util.SoundEvent;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Constant;
import org.spongepowered.asm.mixin.injection.ModifyConstant;
import org.spongepowered.asm.mixin.injection.Redirect;

import javax.annotation.Nullable;

import static dev.necauqua.mods.cm.size.EntitySizeInteractions.*;

@Mixin(RenderGlobal.class)
public abstract class RenderGlobalMixin implements IWorldPreciseEvents {

    private double $cm$size = 1.0;
    private BlockPos $cm$blockPos;
    private Vec3d $cm$pos;

    @Override
    public void playEvent(EntityPlayer player, int type, BlockPos blockPos, int data, double size, Vec3d pos) {
        $cm$size = size;
        $cm$blockPos = blockPos;
        $cm$pos = pos;
        playEvent(null, type, blockPos, data);
        $cm$blockPos = null;
    }

    @Redirect(method = "playEvent", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/multiplayer/WorldClient;playSound(Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/util/SoundEvent;Lnet/minecraft/util/SoundCategory;FFZ)V"))
    void playSound(WorldClient self, BlockPos blockPos, SoundEvent soundIn, SoundCategory category, float volume, float pitch, boolean distanceDelay) {
        Vec3d pos = $cm$pos;
        if (pos == null) {
            self.playSound(blockPos, soundIn, category, volume, pitch, distanceDelay);
            return;
        }
        if ($cm$size != 1.0) {
            ((IWorldPreciseSounds) self).playSound(pos, soundIn, category, volume, pitch, distanceDelay, $cm$size);
        } else {
            self.playSound(pos.x, pos.y, pos.z, soundIn, category, volume, pitch, distanceDelay);
        }
    }

    @ModifyConstant(method = "spawnParticle0(IZZDDDDDD[I)Lnet/minecraft/client/particle/Particle;", constant = @Constant(doubleValue = 1024.0))
    double spawnParticle0(double constant, int particleId, boolean ignoreRange, boolean minParticles, double xCoord, double yCoord, double zCoord, double xSpeed, double ySpeed, double zSpeed, int... parameters) {
        return constant * extractSize(particleId, parameters) * getViewerSize();
    }

    @Redirect(method = "playEvent", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/RenderGlobal;spawnParticle0(IZDDDDDD[I)Lnet/minecraft/client/particle/Particle;"))
    Particle spawnParticle0(RenderGlobal renderGlobal, int particleID, boolean ignoreRange, double xCoord, double yCoord, double zCoord, double xSpeed, double ySpeed, double zSpeed, int[] parameters) {
        BlockPos blockPos = $cm$blockPos;
        Vec3d pos = $cm$pos;
        if (blockPos == null || pos == null) {
            return spawnParticle0(particleID, ignoreRange, xCoord, yCoord, zCoord, xSpeed, ySpeed, zSpeed, parameters);
        }
        return spawnParticle0(
                particleID,
                ignoreRange,
                pos.x + (xCoord - blockPos.getX()) * $cm$size,
                pos.y + (yCoord - blockPos.getY()) * $cm$size,
                pos.z + (zCoord - blockPos.getZ()) * $cm$size,
                xSpeed, ySpeed, zSpeed,
                appendSize(parameters, $cm$size)
        );
    }

    @Redirect(method = "playEvent", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/RenderGlobal;spawnParticle(Lnet/minecraft/util/EnumParticleTypes;DDDDDD[I)V"))
    void spawnParticle(RenderGlobal self, EnumParticleTypes particleIn, double xCoord, double yCoord, double zCoord, double xSpeed, double ySpeed, double zSpeed, int[] parameters) {
        BlockPos blockPos = $cm$blockPos;
        Vec3d pos = $cm$pos;
        if (blockPos == null || pos == null) {
            spawnParticle(particleIn, xCoord, yCoord, zCoord, xSpeed, ySpeed, zSpeed, parameters);
            return;
        }
        spawnParticle(
                particleIn,
                pos.x + (xCoord - blockPos.getX()) * $cm$size,
                pos.y + (yCoord - blockPos.getY()) * $cm$size,
                pos.z + (zCoord - blockPos.getZ()) * $cm$size,
                xSpeed, ySpeed, zSpeed,
                appendSize(parameters, $cm$size)
        );
    }

    @Redirect(method = "playEvent", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/multiplayer/WorldClient;spawnParticle(Lnet/minecraft/util/EnumParticleTypes;DDDDDD[I)V"))
    void spawnParticle(WorldClient self, EnumParticleTypes particleType, double xCoord, double yCoord, double zCoord, double xSpeed, double ySpeed, double zSpeed, int[] parameters) {
        BlockPos blockPos = $cm$blockPos;
        Vec3d pos = $cm$pos;
        if (blockPos == null || pos == null) {
            self.spawnParticle(particleType, xCoord, yCoord, zCoord, xSpeed, ySpeed, zSpeed, parameters);
            return;
        }
        self.spawnParticle(
                particleType,
                pos.x + (xCoord - blockPos.getX()) * $cm$size,
                pos.y + (yCoord - blockPos.getY()) * $cm$size,
                pos.z + (zCoord - blockPos.getZ()) * $cm$size,
                xSpeed, ySpeed, zSpeed,
                appendSize(parameters, $cm$size)
        );
    }

    @Redirect(method = "playEvent", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/multiplayer/WorldClient;spawnParticle(Lnet/minecraft/util/EnumParticleTypes;ZDDDDDD[I)V"))
    void spawnParticle(WorldClient self, EnumParticleTypes particleType, boolean ignoreRange, double xCoord, double yCoord, double zCoord, double xSpeed, double ySpeed, double zSpeed, int[] parameters) {
        BlockPos blockPos = $cm$blockPos;
        Vec3d pos = $cm$pos;
        if (blockPos == null || pos == null) {
            self.spawnParticle(particleType, ignoreRange, xCoord, yCoord, zCoord, xSpeed, ySpeed, zSpeed, parameters);
            return;
        }
        self.spawnParticle(
                particleType,
                ignoreRange,
                pos.x + (xCoord - blockPos.getX()) * $cm$size,
                pos.y + (yCoord - blockPos.getY()) * $cm$size,
                pos.z + (zCoord - blockPos.getZ()) * $cm$size,
                xSpeed, ySpeed, zSpeed,
                appendSize(parameters, $cm$size)
        );
    }

    @Shadow
    public abstract void playEvent(EntityPlayer player, int type, BlockPos blockPosIn, int data);

    @Shadow
    protected abstract void spawnParticle(EnumParticleTypes particleIn, double xCoord, double yCoord, double zCoord, double xSpeed, double ySpeed, double zSpeed, int... parameters);

    @Shadow
    @Nullable
    protected abstract Particle spawnParticle0(int particleID, boolean ignoreRange, double xCoord, double yCoord, double zCoord, double xSpeed, double ySpeed, double zSpeed, int... parameters);
}


<|start_filename|>src/main/java/dev/necauqua/mods/cm/mixin/entity/ai/EntityGhastAIFireballAttackMixin.java<|end_filename|>
/*
 * Copyright (c) 2017-2021 <NAME> <<EMAIL>>
 * Licensed under MIT, see the LICENSE file for details.
 */

package dev.necauqua.mods.cm.mixin.entity.ai;

import dev.necauqua.mods.cm.api.ISized;
import dev.necauqua.mods.cm.api.IWorldPreciseEvents;
import net.minecraft.entity.monster.EntityGhast;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Constant;
import org.spongepowered.asm.mixin.injection.ModifyConstant;
import org.spongepowered.asm.mixin.injection.Redirect;

@Mixin(targets = "net.minecraft.entity.monster.EntityGhast$AIFireballAttack")
public final class EntityGhastAIFireballAttackMixin {

    // fireball offsets
    @ModifyConstant(method = "updateTask()V", constant = {
            @Constant(doubleValue = 0.5),
            @Constant(doubleValue = 4.0),
    })
    double updateTask(double constant) {
        return constant * ((ISized) parentEntity).getSizeCM();
    }

    @ModifyConstant(method = "updateTask()V", constant = @Constant(doubleValue = 4096.0))
    double updateTaskSq(double constant) {
        assert parentEntity.getAttackTarget() != null;
        return constant * ((ISized) parentEntity).getSizeCM() * ((ISized) parentEntity.getAttackTarget()).getSizeCM();
    }

    // sound
    @Redirect(method = "updateTask()V", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/World;playEvent(Lnet/minecraft/entity/player/EntityPlayer;ILnet/minecraft/util/math/BlockPos;I)V"))
    void playEvent(World self, EntityPlayer player, int type, BlockPos pos, int data) {
        double size = ((ISized) parentEntity).getSizeCM();
        ((IWorldPreciseEvents) self).playEvent(null, type, pos, data, size, parentEntity.getPositionEyes(1.0f));
    }

    @Shadow
    @Final
    private EntityGhast parentEntity;
}


<|start_filename|>src/main/java/dev/necauqua/mods/cm/mixin/compat/MetamorphMixin.java<|end_filename|>
/*
 * Copyright (c) 2017-2021 <NAME> <<EMAIL>>
 * Licensed under MIT, see the LICENSE file for details.
 */

package dev.necauqua.mods.cm.mixin.compat;

import dev.necauqua.mods.cm.api.IRenderSized;
import dev.necauqua.mods.cm.api.ISized;
import dev.necauqua.mods.cm.size.IEntityExtras;
import net.minecraft.entity.EntityLivingBase;
import org.spongepowered.asm.mixin.Dynamic;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Pseudo;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.ModifyVariable;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;

import static org.objectweb.asm.Opcodes.IFNE;

@Pseudo
@Mixin(targets = "mchorse.metamorph.api.morphs.AbstractMorph", remap = false)
public final class MetamorphMixin {

    //
    // "This is a total rip-off of EntityPlayer#setSize method"
    //
    // THEN WHY DAFUQ DONT YOU JUST CALL IT, WHY DO I KEEP NEEDING TO FIX OTHER MODS, UGH
    //
    // ok understandable, it is protected and access transformers are unhappy because
    // subclasses override it with the protected modifier and then Java is unhappy ugh
    //

    @Dynamic
    @ModifyVariable(method = "updateSizeDefault(Lnet/minecraft/entity/EntityLivingBase;FFF)V", ordinal = 0, at = @At(value = "JUMP", opcode = IFNE, ordinal = 1))
    private static float updateSizeDefaultWidth(float width, EntityLivingBase target) {
        ((IEntityExtras) target).setOriginalWidthCM(width);
        return (float) (width * ((ISized) target).getSizeCM());
    }

    @Dynamic
    @ModifyVariable(method = "updateSizeDefault(Lnet/minecraft/entity/EntityLivingBase;FFF)V", ordinal = 1, at = @At(value = "JUMP", opcode = IFNE, ordinal = 1))
    private static float updateSizeDefaultHeight(float height, EntityLivingBase target) {
        ((IEntityExtras) target).setOriginalHeightCM(height);
        return (float) (height * ((ISized) target).getSizeCM());
    }

    @Pseudo
    @Mixin(targets = "mchorse.metamorph.api.morphs.EntityMorph", remap = false)
    public static final class EntityMorphMixin {

        // to fix particles
        @Dynamic
        @Inject(method = "update", at = @At("TAIL"))
        void update(EntityLivingBase target, CallbackInfo ci) {
            ((IRenderSized) entity).setRawSizeCM(((ISized) target).getSizeCM());
        }

        @Shadow
        protected EntityLivingBase entity;
    }
}


<|start_filename|>src/main/java/dev/necauqua/mods/cm/mixin/entity/monster/EntityEndermanMixin.java<|end_filename|>
/*
 * Copyright (c) 2017-2021 <NAME> <<EMAIL>>
 * Licensed under MIT, see the LICENSE file for details.
 */

package dev.necauqua.mods.cm.mixin.entity.monster;

import dev.necauqua.mods.cm.mixin.entity.EntityMixin;
import dev.necauqua.mods.cm.size.EntitySizeInteractions;
import net.minecraft.entity.monster.EntityEnderman;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Constant;
import org.spongepowered.asm.mixin.injection.ModifyArg;
import org.spongepowered.asm.mixin.injection.ModifyConstant;

@Mixin(EntityEnderman.class)
public abstract class EntityEndermanMixin extends EntityMixin {

    @ModifyConstant(method = "onLivingUpdate", constant = @Constant(doubleValue = 0.25))
    double onLivingUpdate(double constant) {
        return constant * $cm$size;
    }

    @ModifyArg(method = "onLivingUpdate", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/World;spawnParticle(Lnet/minecraft/util/EnumParticleTypes;DDDDDD[I)V"))
    int[] onLivingUpdate(int[] args) {
        return EntitySizeInteractions.appendSize(args, $cm$size);
    }
}


<|start_filename|>src/main/java/dev/necauqua/mods/cm/mixin/client/RenderMixin.java<|end_filename|>
/*
 * Copyright (c) 2017-2021 <NAME> <<EMAIL>>
 * Licensed under MIT, see the LICENSE file for details.
 */

package dev.necauqua.mods.cm.mixin.client;

import dev.necauqua.mods.cm.Config;
import dev.necauqua.mods.cm.api.IRenderSized;
import dev.necauqua.mods.cm.api.ISized;
import net.minecraft.client.renderer.entity.Render;
import net.minecraft.entity.Entity;
import net.minecraft.util.math.AxisAlignedBB;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.*;

@Mixin(Render.class)
public abstract class RenderMixin {

    protected double $cm$sizeHack = 1.0;

    // scale distance to camera here to make the shadow transparency *exactly* the same
    @ModifyVariable(method = "doRenderShadowAndFire", ordinal = 3, at = @At("STORE"))
    double doRenderShadowAndFire(double d0, Entity entity, double x, double y, double z, float yaw, float partialTicks) {
        return d0 / ($cm$sizeHack = ((IRenderSized) entity).getSizeCM(partialTicks));
    }

    // scale shadow size
    @Redirect(method = "renderShadow", at = @At(value = "FIELD", target = "Lnet/minecraft/client/renderer/entity/Render;shadowSize:F"))
    float renderShadow(Render<?> self, Entity entity, double x, double y, double z, float shadowAlpha, float partialTicks) {
        return Math.min((float) (shadowSize * $cm$sizeHack), Config.maxShadowSize); // well reuse the hack, whatever
    }

    // transparency
    @ModifyConstant(method = "renderShadowSingle", constant = @Constant(doubleValue = 2.0, ordinal = 0))
    double renderShadowSingleConstant(double constant) {
        // hack field exists because we have no reference to entity that casted the shadow here
        return constant * $cm$sizeHack;
    }

    // vertical shadow offset
    @ModifyConstant(method = "renderShadowSingle", constant = @Constant(doubleValue = 0.015625))
    double renderShadowSingleConstantOnlySmall(double constant) {
        // hack field exists because we have no reference to entity that casted the shadow here
        return $cm$sizeHack < 1.0 ? // only for smalls because you actually become smaller than the shadow height lol
                constant * $cm$sizeHack :
                constant; // but don't move it extra up when being big
    }

    // undo the height scaling for renderLabel because we scale the whole model with the GL model matrix in RenderManagerMixin
    @SuppressWarnings("UnresolvedMixinReference") // the parameter is generic so the plugin gets confused
    @Redirect(method = "renderLivingLabel", at = @At(value = "FIELD", target = "Lnet/minecraft/entity/Entity;height:F"))
    float renderLivingLabel(Entity self) {
        return (float) (self.height / ((IRenderSized) self).getSizeCM());
    }

    // extend the render bbox to include shadow height (vanilla fix even I guess)
    @ModifyVariable(method = "shouldRender", at = @At(value = "STORE", ordinal = 0))
    AxisAlignedBB shouldRender(AxisAlignedBB aabb, Entity entity) {
        return aabb.expand(0.0, -((ISized) entity).getSizeCM(), 0.0);
    }

    @Shadow
    protected float shadowSize;
}


<|start_filename|>src/main/java/dev/necauqua/mods/cm/mixin/entity/player/EntityPlayerMPMixin.java<|end_filename|>
/*
 * Copyright (c) 2017-2021 <NAME> <<EMAIL>>
 * Licensed under MIT, see the LICENSE file for details.
 */

package dev.necauqua.mods.cm.mixin.entity.player;

import dev.necauqua.mods.cm.mixin.entity.EntityMixin;
import net.minecraft.entity.player.EntityPlayerMP;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.Constant;
import org.spongepowered.asm.mixin.injection.ModifyConstant;

@Mixin(EntityPlayerMP.class)
public abstract class EntityPlayerMPMixin extends EntityMixin {

    @ModifyConstant(method = "handleFalling", constant = @Constant(doubleValue = 0.20000000298023224D))
    double handleFalling(double constant) {
        return constant * $cm$size;
    }

    // that getPosition fix
    @ModifyConstant(method = "getPosition", constant = @Constant(doubleValue = 0.5))
    double getPosition(double constant) {
        return constant * $cm$size;
    }
}


<|start_filename|>src/main/java/dev/necauqua/mods/cm/mixin/client/RenderManagerMixin.java<|end_filename|>
/*
 * Copyright (c) 2017-2021 <NAME> <<EMAIL>>
 * Licensed under MIT, see the LICENSE file for details.
 */

package dev.necauqua.mods.cm.mixin.client;

import dev.necauqua.mods.cm.api.IRenderSized;
import net.minecraft.client.renderer.GlStateManager;
import net.minecraft.client.renderer.entity.Render;
import net.minecraft.client.renderer.entity.RenderManager;
import net.minecraft.entity.Entity;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.*;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;

@Mixin(RenderManager.class)
public final class RenderManagerMixin {

    private double $cm$sizeHack = 1.0; // idk just a useless optimization I guess

    @Inject(method = "renderEntity", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/entity/Render;doRender(Lnet/minecraft/entity/Entity;DDDFF)V"))
    void renderEntityPre(Entity entity, double x, double y, double z, float yaw, float partialTicks, boolean p_188391_10_, CallbackInfo ci) {
        double size = $cm$sizeHack = ((IRenderSized) entity).getSizeCM(partialTicks);
        GlStateManager.pushMatrix();
        GlStateManager.scale(size, size, size);
    }

    @Inject(method = "renderEntity", at = @At(value = "INVOKE", shift = At.Shift.AFTER, target = "Lnet/minecraft/client/renderer/entity/Render;doRender(Lnet/minecraft/entity/Entity;DDDFF)V"))
    void renderEntityPost(Entity entity, double x, double y, double z, float yaw, float partialTicks, boolean p_188391_10_, CallbackInfo ci) {
        GlStateManager.popMatrix();
    }

    @ModifyArg(method = "renderEntity", index = 1, at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/entity/Render;doRender(Lnet/minecraft/entity/Entity;DDDFF)V"))
    double renderEntityX(Entity entity, double x, double y, double z, float yaw, float partialTicks) {
        return x / $cm$sizeHack;
    }

    @ModifyArg(method = "renderEntity", index = 2, at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/entity/Render;doRender(Lnet/minecraft/entity/Entity;DDDFF)V"))
    double renderEntityY(Entity entity, double x, double y, double z, float yaw, float partialTicks) {
        return y / $cm$sizeHack;
    }

    @ModifyArg(method = "renderEntity", index = 3, at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/entity/Render;doRender(Lnet/minecraft/entity/Entity;DDDFF)V"))
    double renderEntityZ(Entity entity, double x, double y, double z, float yaw, float partialTicks) {
        return z / $cm$sizeHack;
    }

    @ModifyConstant(method = "renderDebugBoundingBox", constant = {
            @Constant(doubleValue = 0.009999999776482582),
            @Constant(doubleValue = 2.0),
    })
    double renderDebugBoundingBox(double constant, Entity entity) {
        return constant * ((IRenderSized) entity).getSizeCM();
    }

    @Redirect(method = "renderMultipass", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/entity/Render;renderMultipass(Lnet/minecraft/entity/Entity;DDDFF)V"))
    void renderMultipass(Render<Entity> self, Entity entity, double x, double y, double z, float yaw, float partialTicks) {
        double size = ((IRenderSized) entity).getSizeCM(partialTicks);
        if (size == 1.0) {
            self.renderMultipass(entity, x, y, z, yaw, partialTicks);
            return;
        }
        GlStateManager.pushMatrix();
        GlStateManager.scale(size, size, size);
        self.renderMultipass(entity, x / size, y / size, z / size, yaw, partialTicks);
        GlStateManager.popMatrix();
    }
}


<|start_filename|>src/main/java/dev/necauqua/mods/cm/mixin/entity/player/EntityPlayerSPMixin.java<|end_filename|>
/*
 * Copyright (c) 2017-2021 <NAME> <<EMAIL>>
 * Licensed under MIT, see the LICENSE file for details.
 */

package dev.necauqua.mods.cm.mixin.entity.player;

import dev.necauqua.mods.cm.mixin.entity.EntityMixin;
import net.minecraft.client.entity.EntityPlayerSP;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.Constant;
import org.spongepowered.asm.mixin.injection.ModifyConstant;

@Mixin(EntityPlayerSP.class)
public abstract class EntityPlayerSPMixin extends EntityMixin {

    // push from blocks vertical aabb offset
    @ModifyConstant(method = "onLivingUpdate", constant = @Constant(doubleValue = 0.5))
    double onLivingUpdate(double constant) {
        return constant * $cm$size;
    }

    // scale the distance between sending movement packets
    @ModifyConstant(method = "onUpdateWalkingPlayer", constant = @Constant(doubleValue = 9.0E-4D))
    double onUpdateWalkingPlayer(double constant) {
        return $cm$size < 1.0 ? // only add precision, not lose it with big sizes
                constant * $cm$size * $cm$size :
                constant;
    }

    @ModifyConstant(method = "updateAutoJump", constant = @Constant(doubleValue = 0.5099999904632568D))
    double updateAutoJump(double constant) {
        return constant * $cm$size;
    }

    @ModifyConstant(method = "updateAutoJump", constant = @Constant(intValue = 1, ordinal = 1))
    int updateAutoJump(int constant) {
        return 0;
    }

    @ModifyConstant(method = "updateAutoJump", constant = {
            @Constant(floatValue = 0.001f),
            @Constant(floatValue = -0.15f, ordinal = 0),
            @Constant(floatValue = 0.75f, ordinal = 0),
            @Constant(floatValue = 1.2f, ordinal = 0),
            @Constant(floatValue = 7.0f, ordinal = 1),
            @Constant(floatValue = 0.5f, ordinal = 1),
    })
    float updateAutoJump(float constant) {
        return (float) (constant * $cm$size);
    }

    // that getPosition fix
    @ModifyConstant(method = "getPosition", constant = @Constant(doubleValue = 0.5))
    double getPosition(double constant) {
        return constant * $cm$size;
    }
}


<|start_filename|>src/main/java/dev/necauqua/mods/cm/mixin/compat/NeatMixin.java<|end_filename|>
/*
 * Copyright (c) 2017-2021 <NAME> <<EMAIL>>
 * Licensed under MIT, see the LICENSE file for details.
 */

package dev.necauqua.mods.cm.mixin.compat;

import dev.necauqua.mods.cm.Config;
import dev.necauqua.mods.cm.api.IRenderSized;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.util.math.MathHelper;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.fml.client.event.ConfigChangedEvent;
import net.minecraftforge.fml.common.eventhandler.EventPriority;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import org.spongepowered.asm.mixin.Dynamic;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Pseudo;
import org.spongepowered.asm.mixin.injection.*;

import java.lang.reflect.Field;

import static dev.necauqua.mods.cm.size.EntitySizeInteractions.getViewerSize;
import static org.objectweb.asm.Opcodes.GETFIELD;
import static org.objectweb.asm.Opcodes.GETSTATIC;

// JESUS I DONT WANT TO COMPILE-DEPEND ON MODS WITH NO MAVEN
// (so it's buildable by CI and/or other people and shit) SO BAD
//
// I AM SORRY I SIN SO MUCH WITH THOSE DOGSHIT WORKAROUNDS
//
// and yeah there are no accessors or shadows or anything for statics in Mixin
@Pseudo
@Mixin(targets = "vazkii.neat.HealthBarRenderer", remap = false)
public final class NeatMixin {

    private static Field $cm$heightAboveField;
    private static Field $cm$backgroundHeightField;
    private static Field $cm$maxDistanceField;
    private static double $cm$heightAbove = 0.6;
    private static int $cm$backgroundHeight = 6;
    private static int $cm$maxDistance = 24;

    static {
        try {
            Class<?> configCls = Class.forName("vazkii.neat.NeatConfig");
            $cm$heightAboveField = configCls.getField("heightAbove");
            $cm$backgroundHeightField = configCls.getField("backgroundHeight");
            $cm$maxDistanceField = configCls.getField("maxDistance");
            MinecraftForge.EVENT_BUS.register(Class.forName("vazkii.neat.HealthBarRenderer").newInstance());
            $cm$heightAbove = $cm$heightAboveField.getDouble(null);
            $cm$backgroundHeight = $cm$backgroundHeightField.getInt(null);
            $cm$maxDistance = $cm$maxDistanceField.getInt(null);
        } catch (ReflectiveOperationException e) {
            e.printStackTrace();
        }
    }

    @SubscribeEvent(priority = EventPriority.LOW)
//    @Unique well default unique behaviour of not adding the method (instead of name-mangling) is meh
    public void $cm$onConfigChanged(ConfigChangedEvent.OnConfigChangedEvent e) {
        if ("neat".equals(e.getModID())) {
            try {
                $cm$heightAbove = $cm$heightAboveField.getDouble(null);
                $cm$backgroundHeight = $cm$backgroundHeightField.getInt(null);
                $cm$maxDistance = $cm$maxDistanceField.getInt(null);
            } catch (IllegalAccessException ignored) {
            }
        }
    }

    @Dynamic
    @ModifyConstant(method = "renderHealthBar", constant = @Constant(floatValue = 0.026666672f))
    float renderHealthBarScale(float constant, EntityLivingBase entity, float partialTicks) {
        // we only disable actual scale change in the config
        // below patches for smooth eye height changing are still applied
        return Config.enableNeatIntegration ?
                (float) (constant * ((IRenderSized) entity).getSizeCM(partialTicks)) :
                constant;
    }

    @Group(name = "manualRemapOmg", min = 1, max = 1)
    @Dynamic
    @Redirect(method = "renderHealthBar", at = @At(value = "FIELD", opcode = GETFIELD, target = "Lnet/minecraft/entity/EntityLivingBase;height:F"), require = 0, expect = 0)
    float renderHealthBarEyeHeight(EntityLivingBase self, EntityLivingBase entity, float partialTicks) {
        IRenderSized sized = (IRenderSized) self;
        return (float) (self.height / sized.getSizeCM() * sized.getSizeCM(partialTicks));
    }

    @Group(name = "manualRemapOmg", min = 1, max = 1)
    @Dynamic
    @Redirect(method = "renderHealthBar", at = @At(value = "FIELD", opcode = GETFIELD, target = "Lnet/minecraft/entity/EntityLivingBase;field_70131_O:F"), require = 0, expect = 0)
    float renderHealthBarEyeHeightObf(EntityLivingBase self, EntityLivingBase entity, float partialTicks) {
        IRenderSized sized = (IRenderSized) self;
        return (float) (self.height / sized.getSizeCM() * sized.getSizeCM(partialTicks));
    }

    // yes I cannot access the field that I redirect and that spiraled to all of the above
    // tbf I *can* with reflection, but this is called every frame hello
    @Dynamic
    @Redirect(method = "renderHealthBar", at = @At(value = "FIELD", opcode = GETSTATIC, target = "Lvazkii/neat/NeatConfig;heightAbove:D"))
    double renderHealthBarOffset(EntityLivingBase entity, float partialTicks) {
        double size = ((IRenderSized) entity).getSizeCM(partialTicks);
        double scaledBgHeight = // literally figured out 'the (ok enough) formula' by trial and error wtf
                size < 1.0 && Config.enableNeatIntegration ?
                        $cm$backgroundHeight * 0.026666672 * size :
                        $cm$backgroundHeight * 0.026666672;
        return ($cm$heightAbove - scaledBgHeight) * size + scaledBgHeight;
    }

    @Dynamic
    @Redirect(method = "renderHealthBar", at = @At(value = "FIELD", opcode = GETSTATIC, target = "Lvazkii/neat/NeatConfig;maxDistance:I"))
    int renderHealthBarMaxDistance(EntityLivingBase entity, float partialTicks) {
        double viewerSize = getViewerSize(partialTicks);
        double size = ((IRenderSized) entity).getSizeCM(partialTicks);
        // this can be replaced with a redirect of entity.getDistance to avoid precision loss,
        // but I don't want to do another manual remap idk
        return Math.max(1, MathHelper.ceil($cm$maxDistance * Math.sqrt(viewerSize * size))); // oh well that mod does not use square dist comparisons
    }
}


<|start_filename|>src/main/java/dev/necauqua/mods/cm/api/IWorldPreciseEvents.java<|end_filename|>
/*
 * Copyright (c) 2017-2021 <NAME> <<EMAIL>>
 * Licensed under MIT, see the LICENSE file for details.
 */

package dev.necauqua.mods.cm.api;

import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World;

/**
 * This interface is a part of the mixin that adds necessary modifications
 * to the vanilla event (some network-sent particles and sounds) system to allow precise world positioning
 * and resizing of events.
 * <p>
 * It is implemented on a {@link World} class by the mixin.
 */
public interface IWorldPreciseEvents {

    /**
     * Same as {@link World#playEvent(int, BlockPos, int)} but carries additional data
     * (which is then sent by the network) which is used in altered client code
     * accorddingly - to use a precise (and not block-indexed) position and to resize the effect.
     */
    void playEvent(EntityPlayer player, int type, BlockPos blockPos, int data, double size, Vec3d pos);
}


<|start_filename|>src/main/java/dev/necauqua/mods/cm/mixin/entity/projectile/EntityEnderEyeMixin.java<|end_filename|>
/*
 * Copyright (c) 2017-2021 <NAME> <<EMAIL>>
 * Licensed under MIT, see the LICENSE file for details.
 */

package dev.necauqua.mods.cm.mixin.entity.projectile;

import dev.necauqua.mods.cm.api.ISized;
import dev.necauqua.mods.cm.mixin.entity.EntityMixin;
import dev.necauqua.mods.cm.size.EntitySizeInteractions;
import net.minecraft.entity.Entity;
import net.minecraft.entity.item.EntityEnderEye;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.*;

import static org.objectweb.asm.Opcodes.GETFIELD;

@Mixin(EntityEnderEye.class)
public abstract class EntityEnderEyeMixin extends EntityMixin {

    @ModifyConstant(method = "onUpdate", constant = {
            @Constant(doubleValue = 0.25),
            @Constant(doubleValue = 0.5),
            @Constant(doubleValue = 0.6),
    })
    double onUpdate(double constant) {
        return constant * $cm$size;
    }

    @ModifyArg(method = "onUpdate", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/World;spawnParticle(Lnet/minecraft/util/EnumParticleTypes;DDDDDD[I)V"))
    int[] onUpdate(int[] args) {
        return EntitySizeInteractions.appendSize(args, $cm$size);
    }

    @Redirect(method = "onUpdate", at = @At(value = "FIELD", opcode = GETFIELD, ordinal = 0, target = "Lnet/minecraft/entity/item/EntityEnderEye;motionX:D"))
    double onUpdateMotionX(EntityEnderEye self) {
        return self.motionX * $cm$size;
    }

    @Redirect(method = "onUpdate", at = @At(value = "FIELD", opcode = GETFIELD, ordinal = 0, target = "Lnet/minecraft/entity/item/EntityEnderEye;motionY:D"))
    double onUpdateMotionY(EntityEnderEye self) {
        return self.motionY * $cm$size;
    }

    @Redirect(method = "onUpdate", at = @At(value = "FIELD", opcode = GETFIELD, ordinal = 0, target = "Lnet/minecraft/entity/item/EntityEnderEye;motionZ:D"))
    double onUpdateMotionZ(EntityEnderEye self) {
        return self.motionZ * $cm$size;
    }

    @ModifyArg(method = "onUpdate", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/World;spawnEntity(Lnet/minecraft/entity/Entity;)Z"))
    Entity onUpdateMotionX(Entity entity) {
        ((ISized) entity).setSizeCM($cm$size);
        return entity;
    }
}


<|start_filename|>src/main/java/dev/necauqua/mods/cm/mixin/client/ParticleManagerMixin.java<|end_filename|>
/*
 * Copyright (c) 2017-2021 <NAME> <<EMAIL>>
 * Licensed under MIT, see the LICENSE file for details.
 */

package dev.necauqua.mods.cm.mixin.client;

import dev.necauqua.mods.cm.api.ISized;
import dev.necauqua.mods.cm.size.EntitySizeInteractions;
import net.minecraft.client.particle.IParticleFactory;
import net.minecraft.client.particle.Particle;
import net.minecraft.client.particle.ParticleManager;
import net.minecraft.world.World;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;

@Mixin(ParticleManager.class)
public final class ParticleManagerMixin {

    @Redirect(method = "spawnEffectParticle", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/particle/IParticleFactory;createParticle(ILnet/minecraft/world/World;DDDDDD[I)Lnet/minecraft/client/particle/Particle;"))
    Particle spawnEffectParticle(IParticleFactory factory, int particleId, World worldIn, double xCoord, double yCoord, double zCoord, double xSpeed, double ySpeed, double zSpeed, int[] parameters) {
        Particle particle = factory.createParticle(particleId, this.world, xCoord, yCoord, zCoord, xSpeed, ySpeed, zSpeed, parameters);
        if (particle == null) {
            return null;
        }
        double size = EntitySizeInteractions.extractSize(particleId, parameters);
        if (size != 1.0) {
            ((ISized) particle).setSizeCM(size);
        }
        return particle;
    }

    @Shadow
    protected World world;
}


<|start_filename|>src/main/java/dev/necauqua/mods/cm/SizeofCommand.java<|end_filename|>
/*
 * Copyright (c) 2017-2021 <NAME> <<EMAIL>>
 * Licensed under MIT, see the LICENSE file for details.
 */

package dev.necauqua.mods.cm;

import dev.necauqua.mods.cm.api.IRenderSized;
import dev.necauqua.mods.cm.size.ChangingSizeProcess;
import net.minecraft.command.*;
import net.minecraft.entity.Entity;
import net.minecraft.server.MinecraftServer;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.text.TextComponentTranslation;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.List;

import static dev.necauqua.mods.cm.ChiseledMe.LOWER_LIMIT;
import static dev.necauqua.mods.cm.ChiseledMe.UPPER_LIMIT;
import static java.lang.String.format;

public final class SizeofCommand extends CommandBase {

    @Override
    @Nonnull
    public String getName() {
        return "sizeof";
    }

    @Override
    @Nonnull
    public String getUsage(@Nonnull ICommandSender sender) {
        return "commands.chiseled_me:sizeof.usage";
    }

    @Override
    public void execute(@Nonnull MinecraftServer server, @Nonnull ICommandSender sender, @Nonnull String[] args) throws CommandException {
        if (args.length == 2 || args.length < 1 || args.length > 5) {
            throw new WrongUsageException(getUsage(sender));
        }
        Entity entity = getEntity(server, sender, args[0]);
        if (args.length == 1) {
            sender.sendMessage(new TextComponentTranslation("commands.chiseled_me:sizeof.get", entity.getDisplayName(), ((IRenderSized) entity).getSizeCM()));
            return;
        }

        double size = ((IRenderSized) entity).getSizeCM();
        double arg = parseDouble(args[2]);

        switch (args[1]) {
            case "set":
                size = arg;
                break;
            case "add":
                size += arg;
                break;
            case "subtract":
                size -= arg;
                break;
            case "multiply":
                size *= arg;
                break;
            case "divide":
                size /= arg;
                break;
            default:
                throw new WrongUsageException(getUsage(sender));
        }
        if (!Config.allowAnySizes) {
            if (size < LOWER_LIMIT) {
                throw new NumberInvalidException("commands.generic.num.tooSmall", format("%.2f", size), LOWER_LIMIT);
            } else if (size > UPPER_LIMIT) {
                throw new NumberInvalidException("commands.generic.num.tooBig", format("%.2f", size), UPPER_LIMIT);
            }
        }
        int lerpTime = 0;
        if (args.length > 3 && !args[3].equals("animate")) {
            throw new WrongUsageException(getUsage(sender));
        }
        if (args.length == 4) {
            lerpTime = ChangingSizeProcess.log2LerpTime(((IRenderSized) entity).getSizeCM(), size);
        } else if (args.length == 5) {
            lerpTime = parseInt(args[4], 0);
        }
        ((IRenderSized) entity).setSizeCM(size, lerpTime);
        sender.sendMessage(new TextComponentTranslation("commands.chiseled_me:sizeof.set", entity.getDisplayName(), size));
    }

    @Override
    @Nonnull
    public List<String> getTabCompletions(MinecraftServer server, ICommandSender sender, String[] args, @Nullable BlockPos pos) {
        String[] completions;
        switch (args.length) {
            case 1:
                completions = server.getOnlinePlayerNames();
                break;
            case 2:
                completions = new String[]{"set", "add", "subtract", "multiply", "divide"};
                break;
            case 4:
                completions = new String[]{"animate"};
                break;
            default:
                completions = new String[0];
        }
        return getListOfStringsMatchingLastWord(args, completions);
    }

    @Override
    public int getRequiredPermissionLevel() {
        return 2;
    }

    @Override
    public boolean isUsernameIndex(String[] args, int index) {
        return index == 0;
    }
}


<|start_filename|>src/main/java/dev/necauqua/mods/cm/mixin/entity/ai/EntitySpiderAISpiderAttack.java<|end_filename|>
/*
 * Copyright (c) 2017-2021 <NAME> <<EMAIL>>
 * Licensed under MIT, see the LICENSE file for details.
 */

package dev.necauqua.mods.cm.mixin.entity.ai;

import dev.necauqua.mods.cm.api.ISized;
import net.minecraft.entity.EntityCreature;
import net.minecraft.entity.ai.EntityAIAttackMelee;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.Constant;
import org.spongepowered.asm.mixin.injection.ModifyConstant;

@Mixin(targets = "net.minecraft.entity.monster.EntitySpider$AISpiderAttack")
public final class EntitySpiderAISpiderAttack extends EntityAIAttackMelee {

    public EntitySpiderAISpiderAttack(EntityCreature creature, double speedIn, boolean useLongMemory) {
        super(creature, speedIn, useLongMemory);
    }

    @ModifyConstant(method = "getAttackReachSqr(Lnet/minecraft/entity/EntityLivingBase;)D", constant = @Constant(floatValue = 4.0f), require = 0, expect = 0)
    float getAttackReachSqr(float constant) {
        double size = ((ISized) attacker).getSizeCM();
        return (float) (constant * size * size);
    }
}


<|start_filename|>src/main/java/dev/necauqua/mods/cm/size/DataSerializerDouble.java<|end_filename|>
/*
 * Copyright (c) 2017-2021 <NAME> <<EMAIL>>
 * Licensed under MIT, see the LICENSE file for details.
 */

package dev.necauqua.mods.cm.size;

import net.minecraft.network.PacketBuffer;
import net.minecraft.network.datasync.DataParameter;
import net.minecraft.network.datasync.DataSerializer;
import net.minecraftforge.event.RegistryEvent;
import net.minecraftforge.fml.common.Mod.EventBusSubscriber;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.registries.DataSerializerEntry;

import static dev.necauqua.mods.cm.ChiseledMe.MODID;
import static dev.necauqua.mods.cm.ChiseledMe.ns;

@EventBusSubscriber(modid = MODID)
public final class DataSerializerDouble implements DataSerializer<Double> {

    public static final DataSerializerDouble INSTANCE = new DataSerializerDouble();

    public void write(PacketBuffer buf, Double value) {
        buf.writeDouble(value);
    }

    public Double read(PacketBuffer buf) {
        return buf.readDouble();
    }

    public DataParameter<Double> createKey(int id) {
        return new DataParameter<>(id, this);
    }

    public Double copyValue(Double value) {
        return value;
    }

    @SubscribeEvent
    public static void on(RegistryEvent.Register<DataSerializerEntry> e) {
        e.getRegistry().register(new DataSerializerEntry(INSTANCE).setRegistryName(ns("double")));
    }
}


<|start_filename|>src/main/java/dev/necauqua/mods/cm/mixin/entity/EntityGuardianMixin.java<|end_filename|>
/*
 * Copyright (c) 2017-2021 <NAME> <<EMAIL>>
 * Licensed under MIT, see the LICENSE file for details.
 */

package dev.necauqua.mods.cm.mixin.entity;

import dev.necauqua.mods.cm.size.EntitySizeInteractions;
import net.minecraft.entity.monster.EntityGuardian;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Constant;
import org.spongepowered.asm.mixin.injection.ModifyArg;
import org.spongepowered.asm.mixin.injection.ModifyConstant;

@Mixin(EntityGuardian.class)
public abstract class EntityGuardianMixin extends EntityMixin {

    @ModifyConstant(method = "onLivingUpdate", constant = @Constant(doubleValue = 1.5))
    double spawnParticles(double constant) {
        return constant * $cm$size;
    }

    @ModifyArg(method = "onLivingUpdate", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/World;spawnParticle(Lnet/minecraft/util/EnumParticleTypes;DDDDDD[I)V"))
    int[] spawnParticles(int[] args) {
        return EntitySizeInteractions.appendSize(args, $cm$size);
    }
}


<|start_filename|>src/main/java/dev/necauqua/mods/cm/api/package-info.java<|end_filename|>
/*
 * Copyright (c) 2017-2021 <NAME> <<EMAIL>>
 * Licensed under MIT, see the LICENSE file for details.
 */

@API(owner = MODID, provides = "chiseled_me_api", apiVersion = "@API_VERSION@")
package dev.necauqua.mods.cm.api;

import net.minecraftforge.fml.common.API;

import static dev.necauqua.mods.cm.ChiseledMe.MODID;


<|start_filename|>src/main/java/dev/necauqua/mods/cm/mixin/client/WorldClientMixin.java<|end_filename|>
package dev.necauqua.mods.cm.mixin.client;

import dev.necauqua.mods.cm.api.ISized;
import dev.necauqua.mods.cm.api.IWorldPreciseSounds;
import net.minecraft.client.Minecraft;
import net.minecraft.client.audio.PositionedSoundRecord;
import net.minecraft.client.multiplayer.WorldClient;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.util.SoundCategory;
import net.minecraft.util.SoundEvent;
import net.minecraft.util.math.Vec3d;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;

import javax.annotation.Nullable;

@Mixin(WorldClient.class)
public abstract class WorldClientMixin implements IWorldPreciseSounds {

    private double $cm$size = 1.0;

    @Override
    public void playSound(Vec3d pos, SoundEvent soundIn, SoundCategory category, float volume, float pitch, boolean distanceDelay, double size) {
        $cm$size = size;
        playSound(pos.x, pos.y, pos.z, soundIn, category, volume, pitch, distanceDelay);
        $cm$size = 1.0;
    }

    @Override
    public void playSound(@Nullable EntityPlayer player, Vec3d pos, SoundEvent soundIn, SoundCategory category, float volume, float pitch, double size) {
        if (player == mc.player) {
            playSound(pos, soundIn, category, volume, pitch, false, size);
        }
    }

    @Redirect(method = "playSound(DDDLnet/minecraft/util/SoundEvent;Lnet/minecraft/util/SoundCategory;FFZ)V", at = @At(value = "NEW", target = "net/minecraft/client/audio/PositionedSoundRecord"))
    PositionedSoundRecord playSound(SoundEvent sound, SoundCategory category, float volume, float pitch, float x, float y, float z) {
        PositionedSoundRecord s = new PositionedSoundRecord(sound, category, volume, pitch, x, y, z);
        //noinspection ConstantConditions we mixin the interface lol
        ((ISized) s).setSizeCM($cm$size);
        return s;
    }

    @Shadow
    @Final
    private Minecraft mc;

    @Shadow
    public abstract void playSound(double x, double y, double z, SoundEvent soundIn, SoundCategory category, float volume, float pitch, boolean distanceDelay);
}


<|start_filename|>src/main/java/dev/necauqua/mods/cm/mixin/item/ItemBoatMixin.java<|end_filename|>
/*
 * Copyright (c) 2017-2021 <NAME> <<EMAIL>>
 * Licensed under MIT, see the LICENSE file for details.
 */

package dev.necauqua.mods.cm.mixin.item;

import dev.necauqua.mods.cm.api.ISized;
import net.minecraft.entity.item.EntityBoat;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.ItemBoat;
import net.minecraft.item.ItemStack;
import net.minecraft.util.ActionResult;
import net.minecraft.util.EnumHand;
import net.minecraft.world.World;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.*;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;

@Mixin(ItemBoat.class)
public final class ItemBoatMixin {

    private static double $cm$hack = 1.0;

    @Inject(method = "onItemRightClick", at = @At("HEAD"))
    void onItemRightClick(World worldIn, EntityPlayer player, EnumHand hand, CallbackInfoReturnable<ActionResult<ItemStack>> cir) {
        $cm$hack = ((ISized) player).getSizeCM();
    }

    @Redirect(method = "onItemRightClick", at = @At(value = "NEW", target = "net/minecraft/entity/item/EntityBoat"))
    EntityBoat onItemRightClick(World worldIn, double x, double y, double z) {
        EntityBoat entityBoat = new EntityBoat(worldIn, x, y, z);
        //noinspection ConstantConditions intellij stfu
        ((ISized) entityBoat).setSizeCM($cm$hack);
        return entityBoat;
    }

    @ModifyConstant(method = "onItemRightClick", constant = {
            @Constant(doubleValue = 5.0),
            @Constant(doubleValue = 1.0, ordinal = 3),
            @Constant(doubleValue = -0.12),
            @Constant(doubleValue = -0.1),
    })
    double isInLava(double constant) {
        return constant * $cm$hack;
    }
}


<|start_filename|>src/main/java/dev/necauqua/mods/cm/mixin/entity/ai/EntityAINearestAttackableTargetMixin.java<|end_filename|>
/*
 * Copyright (c) 2017-2021 <NAME> <<EMAIL>>
 * Licensed under MIT, see the LICENSE file for details.
 */

package dev.necauqua.mods.cm.mixin.entity.ai;

import dev.necauqua.mods.cm.api.ISized;
import net.minecraft.entity.ai.EntityAINearestAttackableTarget;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.Constant;
import org.spongepowered.asm.mixin.injection.ModifyConstant;

@Mixin(EntityAINearestAttackableTarget.class)
public abstract class EntityAINearestAttackableTargetMixin extends EntityAITargetMixin {

    @ModifyConstant(method = "getTargetableArea", constant = @Constant(doubleValue = 4.0))
    double getTargetableArea(double constant) {
        return constant * ((ISized) taskOwner).getSizeCM();
    }
}


<|start_filename|>src/main/java/dev/necauqua/mods/cm/recipes/WeirdBeaconRecipe.java<|end_filename|>
/*
 * Copyright (c) 2017-2021 <NAME> <<EMAIL>>
 * Licensed under MIT, see the LICENSE file for details.
 */

package dev.necauqua.mods.cm.recipes;

import dev.necauqua.mods.cm.Log;
import dev.necauqua.mods.cm.advancements.AdvancementTriggers;
import it.unimi.dsi.fastutil.ints.IntList;
import net.minecraft.inventory.InventoryCrafting;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.item.crafting.CraftingManager;
import net.minecraft.item.crafting.IRecipe;
import net.minecraft.item.crafting.Ingredient;
import net.minecraft.item.crafting.ShapedRecipes;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.util.NonNullList;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.common.ForgeHooks;
import net.minecraftforge.event.RegistryEvent;
import net.minecraftforge.fml.common.Mod.EventBusSubscriber;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.common.registry.GameRegistry.ObjectHolder;
import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;

import javax.annotation.Nullable;

import static dev.necauqua.mods.cm.ChiseledMe.MODID;
import static dev.necauqua.mods.cm.ChiseledMe.ns;
import static net.minecraft.init.Blocks.*;

@EventBusSubscriber(modid = MODID)
public final class WeirdBeaconRecipe extends ShapedRecipes {

    @ObjectHolder("chiseled_me:blue_star")
    private static Item BLUE_STAR;

    private static final ItemStack RESULT = new ItemStack(BEACON);

    private static NonNullList<Ingredient> createIngredients() {
        NonNullList<Ingredient> ingredients = NonNullList.create();

        NBTTagCompound nbt = new NBTTagCompound();
        NBTTagCompound blockTag = new NBTTagCompound();
        blockTag.setByte("chiseled_me:color", (byte) 3);
        nbt.setTag("BlockEntityTag", blockTag);
        RESULT.setTagCompound(nbt);

        Ingredient glass = Ingredient.fromItem(Item.getItemFromBlock(GLASS));
        Ingredient obsidian = Ingredient.fromItem(Item.getItemFromBlock(OBSIDIAN));
        ingredients.add(glass);
        ingredients.add(glass);
        ingredients.add(glass);
        ingredients.add(glass);
        ingredients.add(Ingredient.fromItem(BLUE_STAR));
        ingredients.add(glass);
        ingredients.add(obsidian);
        ingredients.add(obsidian);
        ingredients.add(obsidian);
        return ingredients;
    }

    public WeirdBeaconRecipe() {
        super("", 3, 3, createIngredients(), RESULT);
        setRegistryName(ns("weird_beacon"));
    }

    @Override
    public NonNullList<ItemStack> getRemainingItems(InventoryCrafting inv) {
        AdvancementTriggers.WEIRD_BEACON_CRAFT.trigger(ForgeHooks.getCraftingPlayer());
        return super.getRemainingItems(inv);
    }

    @SubscribeEvent
    public static void on(RegistryEvent.Register<IRecipe> e) {
        e.getRegistry().register(new WeirdBeaconRecipe());

        // disallow the blue star in the standard beacon recipe
        // it is allowed there because its registered as a nether star in ore dictionary
        // and vanilla recipes take precedence over the custom ones
        ShapedRecipes beaconRecipe = (ShapedRecipes) CraftingManager.REGISTRY.getObject(new ResourceLocation("beacon"));
        if (beaconRecipe == null) {
            Log.warn("Did not found the vanilla beacon recipe to fix");
            return;
        }
        NonNullList<Ingredient> ingredients = beaconRecipe.getIngredients();
        Ingredient ore = ingredients.get(4);
        Ingredient fixed = new Ingredient(ore.getMatchingStacks()) {
            @Override
            public ItemStack[] getMatchingStacks() {
                return ore.getMatchingStacks();
            }

            @Override
            @SideOnly(Side.CLIENT)
            public IntList getValidItemStacksPacked() {
                return ore.getValidItemStacksPacked();
            }

            @Override
            public boolean apply(@Nullable ItemStack stack) {
                return (stack == null || stack.getItem() != BLUE_STAR) && ore.apply(stack);
            }
        };
        ingredients.set(4, fixed);
    }
}


<|start_filename|>src/main/java/dev/necauqua/mods/cm/mixin/SPacketParticlesMixin.java<|end_filename|>
/*
 * Copyright (c) 2017-2021 <NAME> <<EMAIL>>
 * Licensed under MIT, see the LICENSE file for details.
 */

package dev.necauqua.mods.cm.mixin;

import net.minecraft.network.PacketBuffer;
import net.minecraft.network.play.server.SPacketParticles;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.ModifyVariable;

import static org.objectweb.asm.Opcodes.ISTORE;

// patch the packet to allow particle arguments array to be any length
@Mixin(SPacketParticles.class)
public final class SPacketParticlesMixin {

    @ModifyVariable(method = "readPacketData", at = @At(value = "STORE", opcode = ISTORE), ordinal = 0)
    int readPacketData(int _original, PacketBuffer buffer) {
        return buffer.readVarInt();
    }

    @ModifyVariable(method = "writePacketData", at = @At(value = "STORE", opcode = ISTORE), ordinal = 0)
    int writePacketData(int _original, PacketBuffer buffer) {
        buffer.writeVarInt(particleArguments.length);
        return particleArguments.length;
    }

    @Shadow
    private int[] particleArguments;
}


<|start_filename|>src/main/java/dev/necauqua/mods/cm/mixin/entity/EntitySubclassesEyeHeightMixin.java<|end_filename|>
/*
 * Copyright (c) 2017-2021 <NAME> <<EMAIL>>
 * Licensed under MIT, see the LICENSE file for details.
 */

package dev.necauqua.mods.cm.mixin.entity;

import net.minecraft.entity.item.EntityTNTPrimed;
import net.minecraft.entity.monster.*;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.ModifyConstant;

// every vanilla entity with hardcoded getEyeHeight, yey
@Mixin({
        EntityWitherSkeleton.class,
        EntityWitch.class,
        EntitySnowman.class,
        EntitySilverfish.class,
        EntityShulker.class,
        EntityGiantZombie.class,
        EntityGhast.class,
        EntityEndermite.class,
        EntityEnderman.class,
        EntityCaveSpider.class,
        AbstractSkeleton.class,
        EntityTNTPrimed.class,
        EntitySpider.class,
})
public abstract class EntitySubclassesEyeHeightMixin extends EntityMixin {

    @ModifyConstant(method = "getEyeHeight")
    float getEyeHeight(float constant) {
        return (float) (constant * $cm$size);
    }
}


<|start_filename|>src/main/java/dev/necauqua/mods/cm/size/IPreciseEffectPacket.java<|end_filename|>
/*
 * Copyright (c) 2017-2021 <NAME> <<EMAIL>>
 * Licensed under MIT, see the LICENSE file for details.
 */

package dev.necauqua.mods.cm.size;

import net.minecraft.util.math.Vec3d;

public interface IPreciseEffectPacket {

    double getSizeCM();

    Vec3d getCoordsCM();

    void populateCM(double size, Vec3d pos);
}


<|start_filename|>src/main/java/dev/necauqua/mods/cm/mixin/entity/EntitySetSizeChecksMixin.java<|end_filename|>
/*
 * Copyright (c) 2017-2021 <NAME> <<EMAIL>>
 * Licensed under MIT, see the LICENSE file for details.
 */

package dev.necauqua.mods.cm.mixin.entity;

import net.minecraft.entity.EntityAgeable;
import net.minecraft.entity.monster.EntityZombie;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.ModifyVariable;

@Mixin({EntityAgeable.class, EntityZombie.class})
public abstract class EntitySetSizeChecksMixin extends EntityMixin {

    // fix them not calling super.setSize at first call of setSize
    @ModifyVariable(method = "setSize", at = @At("STORE"))
    boolean setSize(boolean var) {
        return true;
    }
}


<|start_filename|>src/main/java/dev/necauqua/mods/cm/mixin/client/PlayerControllerMPMixin.java<|end_filename|>
/*
 * Copyright (c) 2017-2021 <NAME> <<EMAIL>>
 * Licensed under MIT, see the LICENSE file for details.
 */

package dev.necauqua.mods.cm.mixin.client;

import dev.necauqua.mods.cm.api.IRenderSized;
import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.PlayerControllerMP;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.Constant;
import org.spongepowered.asm.mixin.injection.ModifyConstant;

@Mixin(PlayerControllerMP.class)
public final class PlayerControllerMPMixin {

    @ModifyConstant(method = "getBlockReachDistance", constant = @Constant(floatValue = 0.5F))
    float getBlockReachDistance(float constant) {
        return (float) (constant * ((IRenderSized) mc.player).getSizeCM());
    }

    @Shadow
    @Final
    private Minecraft mc;
}


<|start_filename|>src/main/java/dev/necauqua/mods/cm/mixin/entity/EntityMixinClient.java<|end_filename|>
/*
 * Copyright (c) 2017-2021 <NAME> <<EMAIL>>
 * Licensed under MIT, see the LICENSE file for details.
 */

package dev.necauqua.mods.cm.mixin.entity;

import dev.necauqua.mods.cm.api.ISized;
import net.minecraft.client.entity.EntityOtherPlayerMP;
import net.minecraft.entity.Entity;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.ModifyVariable;

@Mixin({Entity.class, EntityOtherPlayerMP.class})
public abstract class EntityMixinClient implements ISized {

    @ModifyVariable(method = "isInRangeToRenderDist", ordinal = 1, at = @At(value = "STORE", ordinal = 0))
    double isInRangeToRenderDist(double averageEdgeLength) {
        double size = getSizeCM();
        return size >= 1.0 ? // so big ones should use the default algo which should see the giant hitbox
                averageEdgeLength :
                averageEdgeLength / size; // but small ones fool the algo so it renders even one pixel of them
    }
}


<|start_filename|>src/main/java/dev/necauqua/mods/cm/SidedHandler.java<|end_filename|>
/*
 * Copyright (c) 2017-2021 <NAME> <<EMAIL>>
 * Licensed under MIT, see the LICENSE file for details.
 */

package dev.necauqua.mods.cm;

import dev.necauqua.mods.cm.item.ItemRecalibrator;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.block.model.ModelResourceLocation;
import net.minecraft.client.resources.I18n;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.Item;
import net.minecraft.util.ResourceLocation;
import net.minecraft.world.World;
import net.minecraftforge.client.model.ModelLoader;
import net.minecraftforge.fml.common.SidedProxy;
import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;

import javax.annotation.Nullable;
import java.util.Objects;

import static dev.necauqua.mods.cm.ChiseledMe.MODID;

public abstract class SidedHandler {

    @SidedProxy(modId = MODID)
    public static SidedHandler instance;

    public void registerDefaultModel(Item item) {}

    @Nullable
    public World getClientWorld() {
        return null;
    }

    @Nullable
    public EntityPlayer getClientPlayer() {
        return null;
    }

    public String getLocalization(String key, Object... format) {
        return "";
    }

    public void scheduleClientMainLoopTask(Runnable task) {
    }

    @SideOnly(Side.CLIENT)
    public static final class ClientProxy extends SidedHandler {

        private static final Minecraft mc = Minecraft.getMinecraft();

        @Override
        public void registerDefaultModel(Item item) {
            ResourceLocation registryName = Objects.requireNonNull(item.getRegistryName());
            ModelResourceLocation mrl = new ModelResourceLocation(registryName, "inventory");
            ModelLoader.setCustomModelResourceLocation(item, 0, mrl);

            // I really hope that in next versions the advancement icons would support NBT,
            // so leaving this as a dirty hack (see also property overrides in ItemRecalibrator)
            if (item instanceof ItemRecalibrator) {
                for (int i = 1; i <= 16; i++) {
                    ModelLoader.setCustomModelResourceLocation(item, i, mrl);
                }
            }
        }

        @Override
        public String getLocalization(String key, Object... parameters) {
            return I18n.format(key, parameters);
        }

        @Nullable
        @Override
        public EntityPlayer getClientPlayer() {
            return mc.player;
        }

        @Nullable
        @Override
        public World getClientWorld() {
            return mc.world;
        }

        @Override
        public void scheduleClientMainLoopTask(Runnable task) {
            mc.addScheduledTask(task);
        }
    }

    @SideOnly(Side.SERVER)
    public static final class ServerProxy extends SidedHandler {}
}


<|start_filename|>src/main/java/dev/necauqua/mods/cm/mixin/entity/EntityLivingBaseMixin.java<|end_filename|>
/*
 * Copyright (c) 2017-2021 <NAME> <<EMAIL>>
 * Licensed under MIT, see the LICENSE file for details.
 */

package dev.necauqua.mods.cm.mixin.entity;

import dev.necauqua.mods.cm.Config;
import dev.necauqua.mods.cm.api.ISized;
import dev.necauqua.mods.cm.size.EntitySizeInteractions;
import dev.necauqua.mods.cm.size.SizedReachAttribute;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.ai.attributes.IAttribute;
import net.minecraft.entity.ai.attributes.IAttributeInstance;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.util.DamageSource;
import net.minecraft.util.math.Vec3d;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.*;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;

@Mixin(EntityLivingBase.class)
public abstract class EntityLivingBaseMixin extends EntityMixin {

    // universal reach
    @Inject(method = "getEntityAttribute", at = @At("RETURN"), cancellable = true)
    void getEntityAttribute(IAttribute attribute, CallbackInfoReturnable<IAttributeInstance> cir) {
        if (attribute == EntityPlayer.REACH_DISTANCE) {
            cir.setReturnValue(new SizedReachAttribute(cir.getReturnValue(), this));
        }
    }

    @ModifyVariable(method = "attackEntityFrom", ordinal = 0, at = @At("HEAD"))
    float attackEntityFrom(float amount, DamageSource source) {
        Entity attacker = source.getImmediateSource();
        // scaling only entity-to-entity damage
        if (attacker == null) {
            return amount;
        }
        double attackerSize = ((ISized) attacker).getSizeCM();
        if (Config.scaleDamageDealtSmall && attackerSize < 1.0 || Config.scaleDamageDealtBig && attackerSize > 1.0) {
            amount *= attackerSize;
        }
        if (Config.scaleDamageReceivedSmall && $cm$size < 1.0 || Config.scaleDamageReceivedBig && $cm$size > 1.0) {
            amount /= $cm$size;
        }
        return amount;
    }

    // knockback
    @Redirect(method = "attackEntityFrom", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/EntityLivingBase;knockBack(Lnet/minecraft/entity/Entity;FDD)V"))
    void attackEntityFrom(EntityLivingBase self, Entity attacker, float strength, double xRatio, double zRatio) {
        if (Config.scaleMassSmall && $cm$size < 1.0 || Config.scaleMassBig && $cm$size > 1.0) {
            strength = (float) (strength * ((ISized) attacker).getSizeCM() / $cm$size);
        }
        self.knockBack(attacker, strength, xRatio, zRatio);
    }

    // shield reverse knockback
    @ModifyConstant(method = "blockUsingShield", constant = @Constant(floatValue = 0.5f))
    float blockUsingShield(float constant, EntityLivingBase attacker) {
        double otherSize = ((ISized) attacker).getSizeCM();
        return Config.scaleMassSmall && otherSize < 1.0 || Config.scaleMassBig && otherSize > 1.0 ?
                (float) (constant * otherSize / $cm$size) :
                constant;
    }

    // xp orbs spawn at living death
    @ModifyArg(method = "onDeathUpdate", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/World;spawnEntity(Lnet/minecraft/entity/Entity;)Z"))
    Entity onDeathUpdate(Entity entity) {
        ((ISized) entity).setSizeCM($cm$size);
        return entity;
    }

    // vertical fluid collision offset
    @ModifyConstant(method = "travel", constant = @Constant(doubleValue = 0.6000000238418579))
    double travel(double constant) {
        return constant * $cm$size;
    }

    // limb swing animation
    @ModifyConstant(method = "travel", constant = @Constant(floatValue = 4.0f))
    float travelDiv(float constant) {
        return (float) (constant / $cm$size);
    }

    // that little rotation animation
    @ModifyVariable(method = "onUpdate", ordinal = 0, at = @At(value = "STORE", ordinal = 0))
    float onUpdate(float constant) {
        return (float) (constant / $cm$size / $cm$size);
    }

    // region particles

    @ModifyVariable(method = "onEntityUpdate", ordinal = 0, at = @At("STORE"))
    float onEntityUpdateWaterBubbleX(float f2) {
        return (float) (f2 * $cm$size);
    }

    @ModifyVariable(method = "onEntityUpdate", ordinal = 1, at = @At("STORE"))
    float onEntityUpdateWaterBubbleY(float f) {
        return (float) (f * $cm$size);
    }

    @ModifyVariable(method = "onEntityUpdate", ordinal = 2, at = @At("STORE"))
    float onEntityUpdateWaterBubbleZ(float f1) {
        return (float) (f1 * $cm$size);
    }

    @ModifyArg(method = "onEntityUpdate", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/World;spawnParticle(Lnet/minecraft/util/EnumParticleTypes;DDDDDD[I)V"))
    int[] createRunningParticles(int[] args) {
        return EntitySizeInteractions.appendSize(args, $cm$size);
    }

    @ModifyArg(method = "onDeathUpdate", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/World;spawnParticle(Lnet/minecraft/util/EnumParticleTypes;DDDDDD[I)V"))
    int[] onDeathUpdate(int[] args) {
        return EntitySizeInteractions.appendSize(args, $cm$size);
    }

    @ModifyArg(method = "updateFallState", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/WorldServer;spawnParticle(Lnet/minecraft/util/EnumParticleTypes;DDDIDDDD[I)V"))
    int[] updateFallState(int[] args) {
        return EntitySizeInteractions.appendSize(args, $cm$size);
    }

    @ModifyArg(method = "updatePotionEffects", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/World;spawnParticle(Lnet/minecraft/util/EnumParticleTypes;DDDDDD[I)V"))
    int[] updatePotionEffects(int[] args) {
        return EntitySizeInteractions.appendSize(args, $cm$size);
    }

    @ModifyArg(method = "attemptTeleport", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/World;spawnParticle(Lnet/minecraft/util/EnumParticleTypes;DDDDDD[I)V"))
    int[] attemptTeleport(int[] args) {
        return EntitySizeInteractions.appendSize(args, $cm$size);
    }

    @ModifyVariable(method = "updateItemUse", ordinal = 1, at = @At(value = "STORE", ordinal = 0))
    Vec3d updateItemUse(Vec3d vec3d1) {
        return vec3d1.scale($cm$size);
    }

    @ModifyArg(method = "updateItemUse", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/World;spawnParticle(Lnet/minecraft/util/EnumParticleTypes;DDDDDD[I)V"))
    int[] updateItemUse(int[] args) {
        return EntitySizeInteractions.appendSize(args, $cm$size);
    }

    // endregion
}


<|start_filename|>src/main/java/dev/necauqua/mods/cm/mixin/compat/Rfp2DummyPlayerMixin.java<|end_filename|>
/*
 * Copyright (c) 2017-2021 <NAME> <<EMAIL>>
 * Licensed under MIT, see the LICENSE file for details.
 */

package dev.necauqua.mods.cm.mixin.compat;

import dev.necauqua.mods.cm.api.IRenderSized;
import net.minecraft.client.Minecraft;
import net.minecraft.entity.player.EntityPlayer;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Pseudo;

@Pseudo
@Mixin(targets = "com.rejahtavi.rfp2.EntityPlayerDummy")
public abstract class Rfp2DummyPlayerMixin implements IRenderSized {

    // well we only need to forward getSizeCM(float)
    // but forwarded everything for consistency idk

    @Override
    public double getSizeCM(float partialTick) {
        EntityPlayer player = Minecraft.getMinecraft().player;
        return player != null ?
                ((IRenderSized) player).getSizeCM(partialTick) :
                1.0;
    }

    @Override
    public void setSizeCM(double size, int lerpTime) {
        EntityPlayer player = Minecraft.getMinecraft().player;
        if (player != null) {
            ((IRenderSized) player).setSizeCM(size, lerpTime);
        }
    }

    @Override
    public void setRawSizeCM(double size) {
        EntityPlayer player = Minecraft.getMinecraft().player;
        if (player != null) {
            ((IRenderSized) player).setRawSizeCM(size);
        }
    }

    @Override
    public boolean isResizingCM() {
        EntityPlayer player = Minecraft.getMinecraft().player;
        return player != null && ((IRenderSized) player).isResizingCM();
    }

    @Override
    public double getSizeCM() {
        EntityPlayer player = Minecraft.getMinecraft().player;
        return player != null ?
                ((IRenderSized) player).getSizeCM() :
                1.0;
    }
}


<|start_filename|>src/main/java/dev/necauqua/mods/cm/item/ItemMod.java<|end_filename|>
/*
 * Copyright (c) 2017-2021 <NAME> <<EMAIL>>
 * Licensed under MIT, see the LICENSE file for details.
 */

package dev.necauqua.mods.cm.item;

import dev.necauqua.mods.cm.ChiseledMe;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;

import static dev.necauqua.mods.cm.ChiseledMe.MODID;
import static dev.necauqua.mods.cm.ChiseledMe.ns;

public class ItemMod extends Item {
    private boolean isGlowing = false;

    public ItemMod(String name) {
        setRegistryName(ns(name));
        setUnlocalizedName(MODID + ":" + name);
        setCreativeTab(ChiseledMe.TAB);
    }

    public ItemMod setGlowing() {
        isGlowing = true;
        return this;
    }

    @Override
    public boolean hasEffect(ItemStack stack) {
        return isGlowing || super.hasEffect(stack);
    }
}


<|start_filename|>src/main/java/dev/necauqua/mods/cm/mixin/entity/projectile/ProjectileHelperMixin.java<|end_filename|>
/*
 * Copyright (c) 2017-2021 <NAME> <<EMAIL>>
 * Licensed under MIT, see the LICENSE file for details.
 */

package dev.necauqua.mods.cm.mixin.entity.projectile;

import dev.necauqua.mods.cm.api.ISized;
import net.minecraft.entity.Entity;
import net.minecraft.entity.projectile.ProjectileHelper;
import net.minecraft.util.math.AxisAlignedBB;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.*;

@Mixin(ProjectileHelper.class)
public final class ProjectileHelperMixin {

    private static double $cm$hack = 1.0;

    @ModifyConstant(method = "forwardsRaycast", constant = @Constant(doubleValue = 1.0))
    private static double forwardsRaycast(double constant, Entity projectile) {
        return constant * ((ISized) projectile).getSizeCM();
    }

    @ModifyVariable(method = "forwardsRaycast", ordinal = 3, at = @At("STORE"))
    private static double forwardsRaycastX(double variable, Entity projectile) {
        return variable * ((ISized) projectile).getSizeCM();
    }

    @ModifyVariable(method = "forwardsRaycast", ordinal = 4, at = @At("STORE"))
    private static double forwardsRaycastY(double variable, Entity projectile) {
        return variable * ((ISized) projectile).getSizeCM();
    }

    @ModifyVariable(method = "forwardsRaycast", ordinal = 5, at = @At("STORE"))
    private static double forwardsRaycastZ(double variable, Entity projectile) {
        return variable * ((ISized) projectile).getSizeCM();
    }

    // same as in EntityThrowableAndArrowMixin#onUpdate too
    @Redirect(method = "forwardsRaycast", at = @At(value = "INVOKE", ordinal = 1, target = "Lnet/minecraft/entity/Entity;getEntityBoundingBox()Lnet/minecraft/util/math/AxisAlignedBB;"))
    private static AxisAlignedBB forwardsRaycast(Entity entity) {
        $cm$hack = ((ISized) entity).getSizeCM();
        return entity.getEntityBoundingBox();
    }

    @ModifyConstant(method = "forwardsRaycast", constant = @Constant(doubleValue = 0.30000001192092896))
    private static double forwardsRaycast(double constant) {
        double size = $cm$hack;
        $cm$hack = 1.0;
        return constant * size;
    }

    @ModifyVariable(method = "rotateTowardsMovement", ordinal = 0, at = @At("STORE"))
    private static double rotateTowardsMovementX(double variable, Entity projectile) {
        return variable * ((ISized) projectile).getSizeCM();
    }

    @ModifyVariable(method = "rotateTowardsMovement", ordinal = 1, at = @At("STORE"))
    private static double rotateTowardsMovementY(double variable, Entity projectile) {
        return variable * ((ISized) projectile).getSizeCM();
    }

    @ModifyVariable(method = "rotateTowardsMovement", ordinal = 2, at = @At("STORE"))
    private static double rotateTowardsMovementZ(double variable, Entity projectile) {
        return variable * ((ISized) projectile).getSizeCM();
    }
}


<|start_filename|>src/main/java/dev/necauqua/mods/cm/mixin/compat/OptifineFixMixin.java<|end_filename|>
/*
 * Copyright (c) 2017-2021 <NAME> <<EMAIL>>
 * Licensed under MIT, see the LICENSE file for details.
 */

package dev.necauqua.mods.cm.mixin.compat;

import net.minecraft.entity.item.EntityItem;
import net.minecraft.item.ItemStack;
import net.minecraft.network.datasync.DataParameter;
import org.spongepowered.asm.mixin.Dynamic;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Pseudo;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;

import static org.objectweb.asm.Opcodes.GETSTATIC;

@Pseudo
@Mixin(targets = "net.optifine.DynamicLights", remap = false)
public final class OptifineFixMixin {

    @Dynamic // shut up the plugin
    @Redirect(method = "getItemStack", at = @At(value = "FIELD", opcode = GETSTATIC, target = "Lnet/optifine/DynamicLights;PARAMETER_ITEM_STACK:Lnet/minecraft/network/datasync/DataParameter;"))
    private static DataParameter<ItemStack> getItemStack() {
        return EntityItem.ITEM;
    }
}


<|start_filename|>src/main/java/dev/necauqua/mods/cm/size/SizedReachAttribute.java<|end_filename|>
/*
 * Copyright (c) 2017-2021 <NAME> <<EMAIL>>
 * Licensed under MIT, see the LICENSE file for details.
 */

package dev.necauqua.mods.cm.size;

import dev.necauqua.mods.cm.Config;
import dev.necauqua.mods.cm.api.ISized;
import net.minecraft.entity.ai.attributes.AttributeModifier;
import net.minecraft.entity.ai.attributes.IAttribute;
import net.minecraft.entity.ai.attributes.IAttributeInstance;

import javax.annotation.Nullable;
import java.util.Collection;
import java.util.UUID;

public final class SizedReachAttribute implements IAttributeInstance, ISized {

    private final IAttributeInstance peer;
    private final ISized sized;

    public SizedReachAttribute(IAttributeInstance peer, ISized sized) {
        this.peer = peer;
        this.sized = sized;
    }

    @Override
    public double getAttributeValue() { // the main deal
        double real = peer.getAttributeValue();
        if (real < 1.0 && Config.scaleReachSmall) {
            return real * sized.getSizeCM();
        }
        if (real > 1.0 && Config.scaleReachBig) {
            return real * sized.getSizeCM();
        }
        return real;
    }

    @Override
    public double getSizeCM() {
        return sized.getSizeCM();
    }

    @Override
    public void setSizeCM(double size) {
        sized.setSizeCM(size);
    }

    @Override
    public IAttribute getAttribute() {
        return peer.getAttribute();
    }

    @Override
    public double getBaseValue() {
        return peer.getBaseValue();
    }

    @Override
    public void setBaseValue(double baseValue) {
        peer.setBaseValue(baseValue);
    }

    @Override
    public Collection<AttributeModifier> getModifiersByOperation(int operation) {
        return peer.getModifiersByOperation(operation);
    }

    @Override
    public Collection<AttributeModifier> getModifiers() {
        return peer.getModifiers();
    }

    @Override
    public boolean hasModifier(AttributeModifier modifier) {
        return peer.hasModifier(modifier);
    }

    @Nullable
    @Override
    public AttributeModifier getModifier(UUID uuid) {
        return peer.getModifier(uuid);
    }

    @Override
    public void applyModifier(AttributeModifier modifier) {
        peer.applyModifier(modifier);
    }

    @Override
    public void removeModifier(AttributeModifier modifier) {
        peer.removeModifier(modifier);
    }

    @Override
    public void removeModifier(UUID uuid) {
        peer.removeModifier(uuid);
    }

    @Override
    public void removeAllModifiers() {
        peer.removeAllModifiers();
    }
}


<|start_filename|>src/main/java/dev/necauqua/mods/cm/mixin/NetHandlerPlayServerMixin.java<|end_filename|>
/*
 * Copyright (c) 2017-2021 <NAME> <<EMAIL>>
 * Licensed under MIT, see the LICENSE file for details.
 */

package dev.necauqua.mods.cm.mixin;

import dev.necauqua.mods.cm.api.ISized;
import net.minecraft.entity.Entity;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.network.NetHandlerPlayServer;
import net.minecraftforge.fml.common.Loader;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Constant;
import org.spongepowered.asm.mixin.injection.ModifyConstant;
import org.spongepowered.asm.mixin.injection.Redirect;

@Mixin(NetHandlerPlayServer.class)
public final class NetHandlerPlayServerMixin {

    private static final boolean adjustMovementCheckForSmalls = Loader.isModLoaded("adhooks");

    @ModifyConstant(method = "processPlayer", constant = {
            @Constant(doubleValue = 0.0625, ordinal = 0),
            @Constant(doubleValue = 0.0625, ordinal = 2),
            @Constant(doubleValue = 0.0625, ordinal = 3), // fix for small aabbs
            @Constant(doubleValue = -0.5), // some sort of vertical movement checker
            @Constant(doubleValue = 0.5),  //
            @Constant(doubleValue = -0.03125), // floating checker
            @Constant(doubleValue = -0.55),    //
    })
    double processPlayer(double constant) {
        return constant * ((ISized) player).getSizeCM();
    }

    @ModifyConstant(method = "processPlayer", constant = @Constant(doubleValue = 0.0625, ordinal = 1))
    double processPlayerMovementCheck(double constant) {
        double size = ((ISized) player).getSizeCM();
        return size > 1.0 ?
                Double.MAX_VALUE : // ok, no idea how to properly scale it, just disable the check for big sizes
                adjustMovementCheckForSmalls && size < 1.0 ?
                    constant / size : // idk a dumb patch for that weird mod
                    constant;
    }

    // entity server reach
    @Redirect(method = "processUseEntity", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/player/EntityPlayerMP;getDistanceSq(Lnet/minecraft/entity/Entity;)D"))
    double processUseEntity(EntityPlayerMP self, Entity argument) {
        return self.getDistanceSq(argument) / (((ISized) self).getSizeCM() * ((ISized) argument).getSizeCM());
    }

    // fix dumb server digging check y-offset
    @ModifyConstant(method = "processPlayerDigging", constant = @Constant(doubleValue = 1.5))
    double processPlayerDigging(double constant) {
        return player.getEyeHeight();
    }

    // copy stuff for vehicle movement just in case
    // commented out atm because of some issues with some mod from a huge modpack someone was testing it with

//    @ModifyConstant(method = "processVehicleMove", constant = {
//            @Constant(doubleValue = 0.0625, ordinal = 0),
//            @Constant(doubleValue = 0.0625, ordinal = 2),
//            @Constant(doubleValue = 0.0625, ordinal = 3), // fix for small aabbs
//            @Constant(doubleValue = -0.5), // some sort of vertical movement checker
//            @Constant(doubleValue = 0.5),  //
//            @Constant(doubleValue = -0.03125), // floating checker
//            @Constant(doubleValue = -0.55),    //
//            @Constant(doubleValue = 1.0E-6D), // some vehicle-specific vertical offset
//            @Constant(doubleValue = 100.0D), // vehicle-specific speed check
//    })
//    double processVehicleMove(double constant) {
//        return constant * ((ISized) lowestRiddenEnt).getSizeCM();
//    }
//
//    @ModifyConstant(method = "processVehicleMove", constant = @Constant(doubleValue = 0.0625, ordinal = 1))
//    double processVehicleMoveMovementCheck(double constant) {
//        return ((ISized) player).getSizeCM() > 1.0 ?
//                Double.MAX_VALUE : // ok, no idea how to properly scale it, just disable the check for big sizes
//                constant;
//    }

    @Shadow
    public EntityPlayerMP player;

//    @Shadow
//    private Entity lowestRiddenEnt;
}


<|start_filename|>src/main/resources/assets/chiseled_me/recipes/_factories.json<|end_filename|>
{
  "ingredients": {
    "potion": "dev.necauqua.mods.cm.recipes.PotionIngredientFactory"
  },
  "conditions": {
    "supersmall_enabled": "dev.necauqua.mods.cm.recipes.SupersmallsEnabledCondition",
    "big_enabled": "dev.necauqua.mods.cm.recipes.BigEnabledCondition"
  }
}


<|start_filename|>src/main/java/dev/necauqua/mods/cm/mixin/entity/item/EntityItemMixin.java<|end_filename|>
/*
 * Copyright (c) 2017-2021 <NAME> <<EMAIL>>
 * Licensed under MIT, see the LICENSE file for details.
 */

package dev.necauqua.mods.cm.mixin.entity.item;

import dev.necauqua.mods.cm.mixin.entity.EntityMixin;
import net.minecraft.entity.item.EntityItem;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.*;

@Mixin(EntityItem.class)
public abstract class EntityItemMixin extends EntityMixin {

    // group range is [1;2] because either the first modifier is applied twice to vanilla constants
    // or the second applies once to the spigot localvar

    @Group(name = "searchForOtherItemsNearby", min = 1, max = 2)
    @ModifyConstant(method = "searchForOtherItemsNearby", constant = @Constant(doubleValue = 0.5), require = 0, expect = 0)
    double searchForOtherItemsNearby(double constant) {
        return constant * $cm$size;
    }

    @Group(name = "searchForOtherItemsNearby", min = 1, max = 2)
    @ModifyVariable(method = "searchForOtherItemsNearby", ordinal = 0, at = @At(value = "STORE", ordinal = 0), require = 0, expect = 0)
    double searchForOtherItemsNearbySpigot(double variable) {
        return variable * $cm$size;
    }
}


<|start_filename|>src/main/java/dev/necauqua/mods/cm/mixin/client/GuiInventoryMixin.java<|end_filename|>
/*
 * Copyright (c) 2017-2021 <NAME> <<EMAIL>>
 * Licensed under MIT, see the LICENSE file for details.
 */

package dev.necauqua.mods.cm.mixin.client;

import dev.necauqua.mods.cm.api.IRenderSized;
import net.minecraft.client.gui.inventory.GuiInventory;
import net.minecraft.client.renderer.GlStateManager;
import net.minecraft.entity.EntityLivingBase;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;

@Mixin(GuiInventory.class)
public final class GuiInventoryMixin {

    // cancel out the entity size when rendering it in GUIs
    @Redirect(method = "drawEntityOnScreen", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/GlStateManager;scale(FFF)V"))
    private static void scale(float x, float y, float z,
            int posX, int posY, int scale, float mouseX, float mouseY,
            EntityLivingBase entity) {
        double size = ((IRenderSized) entity).getSizeCM();
        GlStateManager.scale(x / size, y / size, z / size);
    }
}


<|start_filename|>src/main/java/dev/necauqua/mods/cm/mixin/entity/EntityCreatureMixin.java<|end_filename|>
/*
 * Copyright (c) 2017-2021 <NAME> <<EMAIL>>
 * Licensed under MIT, see the LICENSE file for details.
 */

package dev.necauqua.mods.cm.mixin.entity;

import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityCreature;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;

import static dev.necauqua.mods.cm.size.EntitySizeInteractions.getAverageSize;

@Mixin(EntityCreature.class)
public abstract class EntityCreatureMixin extends EntityMixin {

    // leash distances

    @Redirect(method = "updateLeashedState", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/EntityCreature;getDistance(Lnet/minecraft/entity/Entity;)F"))
    float blockUsingShield(EntityCreature self, Entity entity) {
        double size = getAverageSize(self, entity);
        maximumHomeDistance = (float) (5.0 * size); // also fix this, whatever it is
        return (float) (self.getDistance(entity) / size);
    }

    @Shadow
    private float maximumHomeDistance;
}


<|start_filename|>src/main/java/dev/necauqua/mods/cm/mixin/entity/monster/EntityShulkerMixin.java<|end_filename|>
/*
 * Copyright (c) 2017-2021 <NAME> <<EMAIL>>
 * Licensed under MIT, see the LICENSE file for details.
 */

package dev.necauqua.mods.cm.mixin.entity.monster;

import dev.necauqua.mods.cm.mixin.entity.EntityMixin;
import net.minecraft.entity.Entity;
import net.minecraft.entity.MoverType;
import net.minecraft.entity.monster.EntityShulker;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Constant;
import org.spongepowered.asm.mixin.injection.ModifyConstant;
import org.spongepowered.asm.mixin.injection.Redirect;

@Mixin(EntityShulker.class)
public abstract class EntityShulkerMixin extends EntityMixin {

    // omfg
    @ModifyConstant(method = "onUpdate", constant = {
            @Constant(doubleValue = 1.0),
            @Constant(doubleValue = 0.5, ordinal = 6),
            @Constant(doubleValue = 0.5, ordinal = 7),
            @Constant(doubleValue = 0.5, ordinal = 8),
            @Constant(doubleValue = 0.5, ordinal = 9),
            @Constant(doubleValue = 0.5, ordinal = 10),
            @Constant(doubleValue = 0.5, ordinal = 11),
            @Constant(doubleValue = 0.5, ordinal = 12),
            @Constant(doubleValue = 0.5, ordinal = 13),
            @Constant(doubleValue = 0.5, ordinal = 14),
            @Constant(doubleValue = 0.5, ordinal = 15),
            @Constant(doubleValue = 0.5, ordinal = 16),
            @Constant(doubleValue = 0.5, ordinal = 17),
            @Constant(doubleValue = 0.5, ordinal = 18),
            @Constant(doubleValue = 0.5, ordinal = 19),
            @Constant(doubleValue = 0.5, ordinal = 20),
            @Constant(doubleValue = 0.5, ordinal = 21),
            @Constant(doubleValue = 0.5, ordinal = 22),
            @Constant(doubleValue = 0.5, ordinal = 23),
            @Constant(doubleValue = 0.5, ordinal = 24),
            @Constant(doubleValue = 0.5, ordinal = 25),
            @Constant(doubleValue = 0.5, ordinal = 26),
            @Constant(doubleValue = 0.5, ordinal = 27),
            @Constant(doubleValue = 0.5, ordinal = 28),
            @Constant(doubleValue = 0.5, ordinal = 29),
    })
    double onUpdate(double constant) {
        return constant * $cm$size;
    }

    @Redirect(method = "onUpdate", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/Entity;move(Lnet/minecraft/entity/MoverType;DDD)V"))
    void onUpdate(Entity entity, MoverType type, double x, double y, double z) {
        double size = $cm$size;
        entity.move(type, x * size, y * size, z * size);
    }
}


<|start_filename|>src/main/java/dev/necauqua/mods/cm/mixin/entity/projectile/EntityFireballMixin.java<|end_filename|>
/*
 * Copyright (c) 2017-2021 <NAME> <<EMAIL>>
 * Licensed under MIT, see the LICENSE file for details.
 */

package dev.necauqua.mods.cm.mixin.entity.projectile;

import dev.necauqua.mods.cm.mixin.entity.EntityMixin;
import dev.necauqua.mods.cm.size.EntitySizeInteractions;
import net.minecraft.entity.projectile.EntityFireball;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.*;

import static org.objectweb.asm.Opcodes.GETFIELD;

@Mixin(EntityFireball.class)
public abstract class EntityFireballMixin extends EntityMixin {

    @ModifyConstant(method = "onUpdate", constant = {
            @Constant(doubleValue = 0.25),
            @Constant(doubleValue = 0.5),
    })
    double getUpdate(double constant) {
        return constant * $cm$size;
    }

    @ModifyArg(method = "onUpdate", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/World;spawnParticle(Lnet/minecraft/util/EnumParticleTypes;DDDDDD[I)V"))
    int[] onUpdate(int[] args) {
        return EntitySizeInteractions.appendSize(args, $cm$size);
    }

    @Redirect(method = "onUpdate", at = @At(value = "FIELD", opcode = GETFIELD, ordinal = 0, target = "Lnet/minecraft/entity/projectile/EntityFireball;motionX:D"))
    double onUpdateMotionX(EntityFireball self) {
        return self.motionX * $cm$size;
    }

    @Redirect(method = "onUpdate", at = @At(value = "FIELD", opcode = GETFIELD, ordinal = 0, target = "Lnet/minecraft/entity/projectile/EntityFireball;motionY:D"))
    double onUpdateMotionY(EntityFireball self) {
        return self.motionY * $cm$size;
    }

    @Redirect(method = "onUpdate", at = @At(value = "FIELD", opcode = GETFIELD, ordinal = 0, target = "Lnet/minecraft/entity/projectile/EntityFireball;motionZ:D"))
    double onUpdateMotionZ(EntityFireball self) {
        return self.motionZ * $cm$size;
    }
}


<|start_filename|>src/main/java/dev/necauqua/mods/cm/api/ISized.java<|end_filename|>
/*
 * Copyright (c) 2017-2021 <NAME> <<EMAIL>>
 * Licensed under MIT, see the LICENSE file for details.
 */

package dev.necauqua.mods.cm.api;

/**
 * An interface describing something <i>sized</i>, meaning it has a size attribute of type double.
 * This is usually implemented on vanilla types (Entities, Particles etc.) by a mixin.
 * <p>
 * See {@link IRenderSized} for more description and for example usage as a soft dependency.
 *
 * @author necauqua
 * @see IRenderSized
 */
public interface ISized {

    /**
     * @return the size multiplier of this thing
     */
    double getSizeCM();

    /**
     * Sets the size of the this thing.
     * <p>
     * <b>NO CHECKS ARE DONE FOR SIZE CORRECTNESS</b>
     * (e.g. you can set it to NaN or negative and get a bunch of unexpected behavior).
     *
     * @param size the size to be set
     */
    void setSizeCM(double size);
}


<|start_filename|>src/main/resources/assets/chiseled_me/advancements/sexdecuple.json<|end_filename|>
{
  "parent": "chiseled_me:antipolarization",
  "display": {
    "icon": {
      "item": "chiseled_me:recalibrator",
      "data": 16
    },
    "title": {
      "translate": "advancement.chiseled_me:sexdecuple"
    },
    "description": {
      "translate": "advancement.chiseled_me:sexdecuple.desc"
    },
    "frame": "challenge"
  },
  "criteria": {
    "stuff": {
      "trigger": "chiseled_me:size",
      "conditions": {
        "size": 16
      }
    }
  }
}


<|start_filename|>src/main/resources/assets/chiseled_me/advancements/essence.json<|end_filename|>
{
  "parent": "chiseled_me:beginning",
  "display": {
    "icon": {
      "item": "chiseled_me:pym_essence"
    },
    "title": {
      "translate": "advancement.chiseled_me:essence"
    },
    "description": {
      "translate": "advancement.chiseled_me:essence.desc"
    },
    "announce_to_chat": false
  },
  "criteria": {
    "stuff": {
      "trigger": "minecraft:inventory_changed",
      "conditions": {
        "items": [
          {
            "item": "chiseled_me:pym_essence"
          }
        ]
      }
    }
  },
  "rewards": {
    "recipes": [
      "chiseled_me:recalibrator_reset",
      "chiseled_me:recalibrator_reduction_1",
      "chiseled_me:recalibrator_reduction_2",
      "chiseled_me:recalibrator_reduction_3",
      "chiseled_me:recalibrator_reduction_4",
      "chiseled_me:recalibrator_reduction_5",
      "chiseled_me:recalibrator_reduction_6",
      "chiseled_me:recalibrator_reduction_7",
      "chiseled_me:recalibrator_reduction_8",
      "chiseled_me:pym_container_x"
    ]
  }
}


<|start_filename|>src/main/java/dev/necauqua/mods/cm/mixin/compat/CustomNpcFixMixin.java<|end_filename|>
/*
 * Copyright (c) 2017-2021 <NAME> <<EMAIL>>
 * Licensed under MIT, see the LICENSE file for details.
 */

package dev.necauqua.mods.cm.mixin.compat;

import dev.necauqua.mods.cm.api.ISized;
import dev.necauqua.mods.cm.mixin.client.RenderMixin;
import dev.necauqua.mods.cm.mixin.entity.EntityMixin;
import net.minecraft.client.renderer.GlStateManager;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.inventory.Container;
import org.spongepowered.asm.mixin.Dynamic;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Pseudo;
import org.spongepowered.asm.mixin.injection.*;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;

import static org.spongepowered.asm.mixin.injection.At.Shift.AFTER;

@Pseudo
@Mixin(targets = "noppes.npcs.entity.EntityNPCInterface", remap = false)
public abstract class CustomNpcFixMixin extends EntityMixin {

    @Dynamic
    @ModifyConstant(method = "onCollide", constant = @Constant(doubleValue = 1.0))
    double onCollide(double constant) {
        return constant * $cm$size;
    }

    @Group(name = "manualRemapAgainOmg", min = 1, max = 1)
    @Dynamic
    @ModifyConstant(method = "onUpdate", constant = @Constant(doubleValue = 1.0, ordinal = 0), expect = 0, require = 0)
    double onUpdate(double constant) {
        return constant * $cm$size - 1.0; // yeah fixing other mods, as always
    }

    // that dumb superclass thing from @Pseudo documentation does not work
    // (not with EntityMixin nor just with plain Entity,
    // and I know about remap=false in mixin annotation, tried without it ofc)
    @Group(name = "manualRemapAgainOmg", min = 1, max = 1)
    @Dynamic
    @ModifyConstant(method = "func_70071_h_", constant = @Constant(doubleValue = 1.0, ordinal = 0), expect = 0, require = 0)
    double onUpdateObf(double constant) {
        return constant * $cm$size - 1.0;
    }

    @Pseudo
    @Mixin(targets = "noppes.npcs.entity.EntityCustomNpc", remap = false)
    private static abstract class EntityCustomNpcHitboxFix extends EntityMixin {

        @Group(name = "moreManualRemaps", min = 1, max = 1)
        @Dynamic
        @Inject(method = "updateHitbox", at = @At(value = "INVOKE", shift = AFTER, target = "Lnoppes/npcs/entity/EntityCustomNpc;setPosition(DDD)V"), expect = 0, require = 0)
        void updateHitbox(CallbackInfo ci) {
            setSize(width, height);
        }

        @Group(name = "moreManualRemaps", min = 1, max = 1)
        @Dynamic
        @Inject(method = "updateHitbox", at = @At(value = "INVOKE", shift = AFTER, target = "Lnoppes/npcs/entity/EntityCustomNpc;func_70107_b(DDD)V"), expect = 0, require = 0)
        void updateHitboxObf(CallbackInfo ci) {
            setSize(width, height);
        }
    }

    @Pseudo
    @Mixin(targets = {
            "noppes.npcs.entity.EntityNPCInterface",
            "noppes.npcs.entity.EntityNPCGolem",
            "noppes.npcs.entity.EntityNPCEnderman",
            "noppes.npcs.entity.EntityNpcDragon",
            "noppes.npcs.entity.EntityNpcSlime",
    }, remap = false)
    private static abstract class EntityNpcHitboxFixes extends EntityMixin {

        @Dynamic
        @Inject(method = "updateHitbox", at = @At("TAIL"))
        void updateHitbox(CallbackInfo ci) {
            setSize(width, height);
        }
    }

    @Pseudo
    @Mixin(targets = "noppes.npcs.client.renderer.RenderNPCInterface", remap = false)
    private static abstract class RenderFixes extends RenderMixin {

        @Dynamic
        @ModifyConstant(method = "renderName", constant = {
                @Constant(floatValue = 0.06F),
                @Constant(floatValue = 0.666667F),
        }, expect = 0, require = 0)
        float renderName(float constant) {
            return (float) (constant * $cm$sizeHack);
        }

        @Group(name = "crazyManualRemaps", min = 3, max = 3)
        @Dynamic
        @Redirect(method = "renderName", at = @At(value = "FIELD", target = "Lnoppes/npcs/entity/EntityNPCInterface;height:F"), expect = 0, require = 0)
        float renderNameHeight(@Coerce Entity self) {
            return (float) (self.height / ((ISized) self).getSizeCM());
        }

        @Group(name = "crazyManualRemaps", min = 3, max = 3)
        @Dynamic
        @Redirect(method = "renderName", at = @At(value = "FIELD", target = "Lnoppes/npcs/entity/EntityNPCInterface;field_70131_O:F"), expect = 0, require = 0)
        float renderNameHeightObf(@Coerce Entity self) {
            return (float) (self.height / ((ISized) self).getSizeCM());
        }

        @Group(name = "crazyManualRemaps", min = 3, max = 3)
        @Dynamic
        @Redirect(method = "doRenderShadowAndFire", at = @At(value = "FIELD", target = "Lnoppes/npcs/entity/EntityNPCInterface;width:F"), expect = 0, require = 0)
        float doRenderShadowAndFire(@Coerce Entity self) {
            return (float) (self.width / ((ISized) self).getSizeCM());
        }

        @Group(name = "crazyManualRemaps", min = 3, max = 3)
        @Dynamic
        @Redirect(method = "func_76979_b", at = @At(value = "FIELD", target = "Lnoppes/npcs/entity/EntityNPCInterface;field_70130_N:F"), expect = 0, require = 0)
        float doRenderShadowAndFireObf(@Coerce Entity self) {
            return (float) (self.width / ((ISized) self).getSizeCM());
        }
    }

    @Pseudo
    @Mixin(targets = "noppes.npcs.client.gui.util.GuiNPCInterface", remap = false)
    private static abstract class GuiNPCInterfaceMixin {

        @Group(name = "moarRemaps", min = 1, max = 1)
        @Dynamic
        @Redirect(method = "drawNpc(Lnet/minecraft/entity/EntityLivingBase;IIFI)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/GlStateManager;scale(FFF)V"), expect = 0, require = 0)
        void drawNpc(float x, float y, float z, EntityLivingBase entity) {
            double s = ((ISized) entity).getSizeCM();
            GlStateManager.scale(x / s, y / s, z / s);
        }

        @Group(name = "moarRemaps", min = 1, max = 1)
        @Dynamic
        @Redirect(method = "drawNpc(Lnet/minecraft/entity/EntityLivingBase;IIFI)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/GlStateManager;func_179152_a(FFF)V"), expect = 0, require = 0)
        void drawNpcObf(float x, float y, float z, EntityLivingBase entity) {
            double s = ((ISized) entity).getSizeCM();
            GlStateManager.scale(x / s, y / s, z / s);
        }
    }

    @Pseudo
    @Mixin(targets = "noppes.npcs.client.gui.util.GuiContainerNPCInterface", remap = false)
    private static abstract class GuiContainerNPCInterfaceMixin {

        private ISized $cm$sized; // can't shadow unavailable type apparently

        @Dynamic
        @Inject(method = "<init>", at = @At("TAIL"))
        void constructor(@Coerce ISized npc, Container cont, CallbackInfo ci) {
            $cm$sized = npc;
        }

        @Group(name = "moarRemaps2", min = 1, max = 1)
        @Dynamic
        @Redirect(method = "drawNpc", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/GlStateManager;scale(FFF)V"), expect = 0, require = 0)
        void drawNpc(float x, float y, float z) {
            double s = $cm$sized.getSizeCM();
            GlStateManager.scale(x / s, y / s, z / s);
        }

        @Group(name = "moarRemaps2", min = 1, max = 1)
        @Dynamic
        @Redirect(method = "drawNpc", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/GlStateManager;func_179152_a(FFF)V"), expect = 0, require = 0)
        void drawNpcObf(float x, float y, float z) {
            double s = $cm$sized.getSizeCM();
            GlStateManager.scale(x / s, y / s, z / s);
        }
    }
}


<|start_filename|>src/main/java/dev/necauqua/mods/cm/mixin/entity/EntityAgeableMixin.java<|end_filename|>
/*
 * Copyright (c) 2017-2021 <NAME> <<EMAIL>>
 * Licensed under MIT, see the LICENSE file for details.
 */

package dev.necauqua.mods.cm.mixin.entity;

import dev.necauqua.mods.cm.api.ISized;
import dev.necauqua.mods.cm.size.EntitySizeInteractions;
import net.minecraft.entity.EntityAgeable;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.*;

@Mixin(EntityAgeable.class)
public abstract class EntityAgeableMixin extends EntityMixin {

    @ModifyVariable(method = "processInteract", at = @At("STORE"))
    EntityAgeable processInteract(EntityAgeable child) {
        if (child != null) {
            ((ISized) child).setSizeCM($cm$size);
        }
        return child;
    }

    @ModifyConstant(method = "onLivingUpdate", constant = @Constant(doubleValue = 0.5))
    double onLivingUpdate(double constant) {
        return constant * $cm$size;
    }

    @ModifyArg(method = "onLivingUpdate", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/World;spawnParticle(Lnet/minecraft/util/EnumParticleTypes;DDDDDD[I)V"))
    int[] attemptTeleport(int[] args) {
        return EntitySizeInteractions.appendSize(args, $cm$size);
    }
}


<|start_filename|>src/main/java/dev/necauqua/mods/cm/mixin/TileEntityBeaconMixin.java<|end_filename|>
/*
 * Copyright (c) 2017-2021 <NAME> <<EMAIL>>
 * Licensed under MIT, see the LICENSE file for details.
 */

package dev.necauqua.mods.cm.mixin;

import net.minecraft.item.EnumDyeColor;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.tileentity.TileEntityBeacon;
import org.spongepowered.asm.mixin.Dynamic;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Group;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.Redirect;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;

@Mixin(TileEntityBeacon.class)
public final class TileEntityBeaconMixin {

    private EnumDyeColor $cm$baseColor = EnumDyeColor.WHITE;

    @Group(name = "beaconColor", min = 1, max = 1)
    @Redirect(method = "updateSegmentColors", at = @At(value = "FIELD", target = "Lnet/minecraft/item/EnumDyeColor;WHITE:Lnet/minecraft/item/EnumDyeColor;"), expect = 0)
    EnumDyeColor beaconColor() {
        return $cm$baseColor;
    }

    @Group(name = "beaconColor", min = 1, max = 1)
    @Dynamic
    @Redirect(method = "updateGlassLayers", remap = false, at = @At(value = "FIELD", target = "Lnet/minecraft/item/EnumDyeColor;WHITE:Lnet/minecraft/item/EnumDyeColor;"), expect = 0)
    EnumDyeColor beaconColorBetterFps() {
        return $cm$baseColor;
    }

    @Inject(method = "readFromNBT", at = @At("TAIL"))
    void readFromNBT(NBTTagCompound nbt, CallbackInfo ci) {
        $cm$baseColor = EnumDyeColor.byMetadata(nbt.getByte("chiseled_me:color"));
    }

    @Inject(method = "writeToNBT", at = @At("TAIL"))
    void writeToNBT(NBTTagCompound nbt, CallbackInfoReturnable<NBTTagCompound> cir) {
        nbt.setByte("chiseled_me:color", (byte) $cm$baseColor.getMetadata());
    }
}


<|start_filename|>src/main/java/dev/necauqua/mods/cm/mixin/entity/item/EntityEnderPearlMixin.java<|end_filename|>
/*
 * Copyright (c) 2017-2021 <NAME> <<EMAIL>>
 * Licensed under MIT, see the LICENSE file for details.
 */

package dev.necauqua.mods.cm.mixin.entity.item;

import dev.necauqua.mods.cm.api.ISized;
import dev.necauqua.mods.cm.mixin.entity.EntityMixin;
import dev.necauqua.mods.cm.size.EntitySizeInteractions;
import net.minecraft.entity.Entity;
import net.minecraft.entity.item.EntityEnderPearl;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.*;

@Mixin(EntityEnderPearl.class)
public abstract class EntityEnderPearlMixin extends EntityMixin {

    @ModifyArg(method = "onImpact", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/World;spawnParticle(Lnet/minecraft/util/EnumParticleTypes;DDDDDD[I)V"))
    int[] onImpact(int[] args) {
        return EntitySizeInteractions.appendSize(args, $cm$size);
    }

    @ModifyConstant(method = "onImpact", constant = @Constant(doubleValue = 2.0))
    double onImpact(double constant) {
        return constant * $cm$size;
    }

    @Group(name = "onImpact", min = 1, max = 1)
    @ModifyArg(method = "onImpact", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/World;spawnEntity(Lnet/minecraft/entity/Entity;)Z"), expect = 0, require = 0)
    Entity onImpact(Entity entity) {
        ((ISized) entity).setSizeCM($cm$size);
        return entity;
    }

    // method added by spigot and EnderPearlEntity patched to use it directly (to have SpawnReason.ENDER_PEARL)
    @SuppressWarnings("UnresolvedMixinReference")
    @Group(name = "onImpact", min = 1, max = 1)
    @ModifyArg(method = "onImpact", at = @At(value = "INVOKE", remap = false, target = "Lnet/minecraft/world/World;addEntity(Lnet/minecraft/entity/Entity;Lorg/bukkit/event/entity/CreatureSpawnEvent$SpawnReason;)Z"), expect = 0, require = 0)
    Entity onImpactSpigot(Entity entity) {
        ((ISized) entity).setSizeCM($cm$size);
        return entity;
    }
}


<|start_filename|>src/main/java/dev/necauqua/mods/cm/mixin/entity/EntityVillagerMixin.java<|end_filename|>
/*
 * Copyright (c) 2017-2021 <NAME> <<EMAIL>>
 * Licensed under MIT, see the LICENSE file for details.
 */

package dev.necauqua.mods.cm.mixin.entity;

import dev.necauqua.mods.cm.size.EntitySizeInteractions;
import net.minecraft.entity.passive.EntityVillager;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Constant;
import org.spongepowered.asm.mixin.injection.ModifyArg;
import org.spongepowered.asm.mixin.injection.ModifyConstant;

@Mixin(EntityVillager.class)
public abstract class EntityVillagerMixin extends EntityMixin {

    @ModifyConstant(method = "spawnParticles", constant = @Constant(doubleValue = 1.0))
    double spawnParticles(double constant) {
        return constant * $cm$size;
    }

    @ModifyArg(method = "spawnParticles", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/World;spawnParticle(Lnet/minecraft/util/EnumParticleTypes;DDDDDD[I)V"))
    int[] spawnParticles(int[] args) {
        return EntitySizeInteractions.appendSize(args, $cm$size);
    }
}


<|start_filename|>src/main/java/dev/necauqua/mods/cm/mixin/compat/ClassloadingHackMixin.java<|end_filename|>
/*
 * Copyright (c) 2017-2021 <NAME> <<EMAIL>>
 * Licensed under MIT, see the LICENSE file for details.
 */

package dev.necauqua.mods.cm.mixin.compat;

import net.minecraftforge.fml.common.Loader;
import net.minecraftforge.fml.common.ModClassLoader;
import net.minecraftforge.fml.common.ModContainer;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.MixinEnvironment;
import org.spongepowered.asm.mixin.Mixins;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.transformer.MixinProcessor;
import org.spongepowered.asm.mixin.transformer.Proxy;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

@Mixin(value = Loader.class, remap = false)
public final class ClassloadingHackMixin {

    // yeah almost completely stolen from VanillaFix as the only way
    // to mixin into other mods in 1.12 that I know of

    @Inject(method = "loadMods", at = @At(value = "INVOKE", ordinal = 1, target = "Lnet/minecraftforge/fml/common/LoadController;transition(Lnet/minecraftforge/fml/common/LoaderState;Z)V"))
    private void theGrossestHackFromVanillaFixThatMumfreyDoesNotApprove(List<String> injectedModContainers, CallbackInfo ci) {

        // yeah technically if both vanillafix and this are present then this completely runs for the second time

        Set<URL> alreadyThere = new HashSet<>(Arrays.asList(modClassLoader.getURLs()));
        for (ModContainer mod : mods) {
            try {
                if (!alreadyThere.contains(mod.getSource().toURI().toURL())) {
                    modClassLoader.addFile(mod.getSource());
                }
            } catch (MalformedURLException e) {
                throw new RuntimeException(e);
            }
        }

        Mixins.addConfiguration("chiseled-me.compat.mixins.json");

        try {
            Field transformerField = Proxy.class.getDeclaredField("transformer");
            transformerField.setAccessible(true);
            Object transformer = transformerField.get(null);

            Field processorField = transformer.getClass().getDeclaredField("processor");
            processorField.setAccessible(true);
            Object processor = processorField.get(transformer);

            Method selectConfigsMethod = MixinProcessor.class.getDeclaredMethod("selectConfigs", MixinEnvironment.class);
            Method prepareConfigsMethod = MixinProcessor.class.getDeclaredMethod("prepareConfigs", MixinEnvironment.class);
            selectConfigsMethod.setAccessible(true);
            prepareConfigsMethod.setAccessible(true);

            MixinEnvironment environment = MixinEnvironment.getCurrentEnvironment();
            selectConfigsMethod.invoke(processor, environment);
            prepareConfigsMethod.invoke(processor, environment);

        } catch (ReflectiveOperationException e) {
            throw new RuntimeException(e);
        }
    }

    @Shadow
    private ModClassLoader modClassLoader;

    @Shadow
    private List<ModContainer> mods;
}


<|start_filename|>src/main/java/dev/necauqua/mods/cm/advancements/StatelessTrigger.java<|end_filename|>
/*
 * Copyright (c) 2017-2021 <NAME> <<EMAIL>>
 * Licensed under MIT, see the LICENSE file for details.
 */

package dev.necauqua.mods.cm.advancements;

import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonObject;
import net.minecraft.advancements.ICriterionInstance;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.util.ResourceLocation;

public final class StatelessTrigger extends AdvancementTrigger<StatelessTrigger.Instance> {
    public StatelessTrigger(ResourceLocation id) {
        super(id);
    }

    @Override
    public StatelessTrigger.Instance deserializeInstance(JsonObject json, JsonDeserializationContext context) {
        return new Instance(id);
    }

    public void trigger(EntityPlayer player) {
        trigger(player, $ -> true);
    }

    public static final class Instance implements ICriterionInstance {
        private final ResourceLocation id;

        public Instance(ResourceLocation id) {
            this.id = id;
        }

        @Override
        public ResourceLocation getId() {
            return id;
        }
    }
}


<|start_filename|>src/main/java/dev/necauqua/mods/cm/mixin/entity/projectile/EntityThrowableMixin.java<|end_filename|>
/*
 * Copyright (c) 2017-2021 <NAME> <<EMAIL>>
 * Licensed under MIT, see the LICENSE file for details.
 */

package dev.necauqua.mods.cm.mixin.entity.projectile;

import dev.necauqua.mods.cm.api.ISized;
import dev.necauqua.mods.cm.mixin.entity.EntityMixin;
import net.minecraft.entity.Entity;
import net.minecraft.entity.projectile.EntityThrowable;
import net.minecraft.util.math.AxisAlignedBB;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Constant;
import org.spongepowered.asm.mixin.injection.ModifyConstant;
import org.spongepowered.asm.mixin.injection.Redirect;

import static org.objectweb.asm.Opcodes.GETFIELD;

@Mixin(EntityThrowable.class)
public abstract class EntityThrowableMixin extends EntityMixin {

    private double $cm$hack = 1.0;

    @ModifyConstant(method = "onUpdate", constant = @Constant(doubleValue = 1.0))
    double onUpdate(double constant) {
        return constant * $cm$size;
    }

    @Redirect(method = "onUpdate", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/math/AxisAlignedBB;expand(DDD)Lnet/minecraft/util/math/AxisAlignedBB;"))
    AxisAlignedBB onUpdate(AxisAlignedBB aabb, double x, double y, double z) {
        return aabb.expand(x * $cm$size, y * $cm$size, z * $cm$size);
    }

    @Redirect(method = "onUpdate", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/Entity;getEntityBoundingBox()Lnet/minecraft/util/math/AxisAlignedBB;"))
    AxisAlignedBB onUpdate(Entity entity) {
        $cm$hack = ((ISized) entity).getSizeCM();
        return entity.getEntityBoundingBox();
    }

    @ModifyConstant(method = "onUpdate", constant = @Constant(doubleValue = 0.30000001192092896))
    double onUpdate2(double constant) {
        double size = $cm$hack;
        $cm$hack = 1.0;
        return constant * size;
    }

    // region onUpdate motionX redirects
    @Redirect(method = "onUpdate", at = @At(value = "FIELD", opcode = GETFIELD, ordinal = 1, target = "Lnet/minecraft/entity/projectile/EntityThrowable;motionX:D"))
    double onUpdateMotionX1(EntityThrowable self) {
        return self.motionX * $cm$size;
    }

    @Redirect(method = "onUpdate", at = @At(value = "FIELD", opcode = GETFIELD, ordinal = 2, target = "Lnet/minecraft/entity/projectile/EntityThrowable;motionX:D"))
    double onUpdateMotionX2(EntityThrowable self) {
        return self.motionX * $cm$size;
    }

    @Redirect(method = "onUpdate", at = @At(value = "FIELD", opcode = GETFIELD, ordinal = 3, target = "Lnet/minecraft/entity/projectile/EntityThrowable;motionX:D"))
    double onUpdateMotionX3(EntityThrowable self) {
        return self.motionX * $cm$size;
    }

    @Redirect(method = "onUpdate", at = @At(value = "FIELD", opcode = GETFIELD, ordinal = 4, target = "Lnet/minecraft/entity/projectile/EntityThrowable;motionX:D"))
    double onUpdateMotionX4(EntityThrowable self) {
        return self.motionX * $cm$size;
    }

    @Redirect(method = "onUpdate", at = @At(value = "FIELD", opcode = GETFIELD, ordinal = 8, target = "Lnet/minecraft/entity/projectile/EntityThrowable;motionX:D"))
    double onUpdateMotionX8(EntityThrowable self) {
        return self.motionX * $cm$size;
    }

    @Redirect(method = "onUpdate", at = @At(value = "FIELD", opcode = GETFIELD, ordinal = 9, target = "Lnet/minecraft/entity/projectile/EntityThrowable;motionX:D"))
    double onUpdateMotionX9(EntityThrowable self) {
        return self.motionX * $cm$size;
    }
    // endregion

    // region onUpdate motionZ redirects
    @Redirect(method = "onUpdate", at = @At(value = "FIELD", opcode = GETFIELD, ordinal = 1, target = "Lnet/minecraft/entity/projectile/EntityThrowable;motionZ:D"))
    double onUpdateMotionZ1(EntityThrowable self) {
        return self.motionZ * $cm$size;
    }

    @Redirect(method = "onUpdate", at = @At(value = "FIELD", opcode = GETFIELD, ordinal = 2, target = "Lnet/minecraft/entity/projectile/EntityThrowable;motionZ:D"))
    double onUpdateMotionZ2(EntityThrowable self) {
        return self.motionZ * $cm$size;
    }

    @Redirect(method = "onUpdate", at = @At(value = "FIELD", opcode = GETFIELD, ordinal = 3, target = "Lnet/minecraft/entity/projectile/EntityThrowable;motionZ:D"))
    double onUpdateMotionZ3(EntityThrowable self) {
        return self.motionZ * $cm$size;
    }

    @Redirect(method = "onUpdate", at = @At(value = "FIELD", opcode = GETFIELD, ordinal = 4, target = "Lnet/minecraft/entity/projectile/EntityThrowable;motionZ:D"))
    double onUpdateMotionZ4(EntityThrowable self) {
        return self.motionZ * $cm$size;
    }

    @Redirect(method = "onUpdate", at = @At(value = "FIELD", opcode = GETFIELD, ordinal = 8, target = "Lnet/minecraft/entity/projectile/EntityThrowable;motionZ:D"))
    double onUpdateMotionZ8(EntityThrowable self) {
        return self.motionZ * $cm$size;
    }

    @Redirect(method = "onUpdate", at = @At(value = "FIELD", opcode = GETFIELD, ordinal = 9, target = "Lnet/minecraft/entity/projectile/EntityThrowable;motionZ:D"))
    double onUpdateMotionZ9(EntityThrowable self) {
        return self.motionZ * $cm$size;
    }
    // endregion

    // region onUpdate motionY redirects
    @Redirect(method = "onUpdate", at = @At(value = "FIELD", opcode = GETFIELD, ordinal = 1, target = "Lnet/minecraft/entity/projectile/EntityThrowable;motionY:D"))
    double onUpdateMotionY1(EntityThrowable self) {
        return self.motionY * $cm$size;
    }

    @Redirect(method = "onUpdate", at = @At(value = "FIELD", opcode = GETFIELD, ordinal = 2, target = "Lnet/minecraft/entity/projectile/EntityThrowable;motionY:D"))
    double onUpdateMotionY2(EntityThrowable self) {
        return self.motionY * $cm$size;
    }

    @Redirect(method = "onUpdate", at = @At(value = "FIELD", opcode = GETFIELD, ordinal = 3, target = "Lnet/minecraft/entity/projectile/EntityThrowable;motionY:D"))
    double onUpdateMotionY3(EntityThrowable self) {
        return self.motionY * $cm$size;
    }

    @Redirect(method = "onUpdate", at = @At(value = "FIELD", opcode = GETFIELD, ordinal = 4, target = "Lnet/minecraft/entity/projectile/EntityThrowable;motionY:D"))
    double onUpdateMotionY4(EntityThrowable self) {
        return self.motionY * $cm$size;
    }

    @Redirect(method = "onUpdate", at = @At(value = "FIELD", opcode = GETFIELD, ordinal = 6, target = "Lnet/minecraft/entity/projectile/EntityThrowable;motionY:D"))
    double onUpdateMotionY6(EntityThrowable self) {
        return self.motionY * $cm$size;
    }

    @Redirect(method = "onUpdate", at = @At(value = "FIELD", opcode = GETFIELD, ordinal = 7, target = "Lnet/minecraft/entity/projectile/EntityThrowable;motionY:D"))
    double onUpdateMotionY7(EntityThrowable self) {
        return self.motionY * $cm$size;
    }
    // endregion
}


<|start_filename|>src/main/java/dev/necauqua/mods/cm/mixin/entity/projectile/EntityThrowableAndArrowMixin.java<|end_filename|>
/*
 * Copyright (c) 2017-2021 <NAME> <<EMAIL>>
 * Licensed under MIT, see the LICENSE file for details.
 */

package dev.necauqua.mods.cm.mixin.entity.projectile;

import dev.necauqua.mods.cm.api.ISized;
import dev.necauqua.mods.cm.mixin.entity.EntityMixin;
import dev.necauqua.mods.cm.size.EntitySizeInteractions;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.projectile.EntityArrow;
import net.minecraft.entity.projectile.EntityThrowable;
import net.minecraft.world.World;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Constant;
import org.spongepowered.asm.mixin.injection.ModifyArg;
import org.spongepowered.asm.mixin.injection.ModifyConstant;

@Mixin({EntityThrowable.class, EntityArrow.class})
public abstract class EntityThrowableAndArrowMixin extends EntityMixin {

    @ModifyConstant(method = "<init>(Lnet/minecraft/world/World;Lnet/minecraft/entity/EntityLivingBase;)V", constant = @Constant(doubleValue = 0.10000000149011612))
    private static double constructor(double constant, World world, EntityLivingBase shooter) {
        return constant * ((ISized) shooter).getSizeCM();
    }

    // water bubble particles motion
    @ModifyConstant(method = "onUpdate", constant = @Constant(doubleValue = 0.25))
    double onUpdate(double constant) {
        return constant * $cm$size;
    }

    // also crit particles in EntityArrow
    @ModifyArg(method = "onUpdate", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/World;spawnParticle(Lnet/minecraft/util/EnumParticleTypes;DDDDDD[I)V"))
    int[] onUpdate(int[] args) {
        return EntitySizeInteractions.appendSize(args, $cm$size);
    }
}


<|start_filename|>src/main/java/dev/necauqua/mods/cm/mixin/compat/GCEntityClientPlayerMPMixin.java<|end_filename|>
/*
 * Copyright (c) 2017-2021 <NAME> <<EMAIL>>
 * Licensed under MIT, see the LICENSE file for details.
 */

package dev.necauqua.mods.cm.mixin.compat;

import com.mojang.authlib.GameProfile;
import dev.necauqua.mods.cm.api.ISized;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.world.World;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Pseudo;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Constant;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.ModifyConstant;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;

@Pseudo
@Mixin(targets = "micdoodle8.mods.galacticraft.core.entities.player.GCEntityClientPlayerMP")
@SuppressWarnings("UnresolvedMixinReference") // plugin is confused about pseudo mixins
public abstract class GCEntityClientPlayerMPMixin extends EntityPlayer implements ISized {

    public GCEntityClientPlayerMPMixin(World world, GameProfile gameProfile) {
        super(world, gameProfile);
    }

    @Inject(method = "getEyeHeight()F", at = @At("RETURN"), cancellable = true)
    void getEyeHeight(CallbackInfoReturnable<Float> ci) {
        ci.setReturnValue((float) (ci.getReturnValueF() * getSizeCM()));
    }

    // scale the distance between sending movement packets
    @ModifyConstant(method = "onLivingUpdate()V", constant = {
            @Constant(doubleValue = 0.00001),
            @Constant(doubleValue = 0.5, ordinal = 0),
            @Constant(doubleValue = 0.5, ordinal = 1),
            @Constant(doubleValue = 0.5, ordinal = 2),
            @Constant(doubleValue = 0.5, ordinal = 3), // without the fifth one
            @Constant(doubleValue = 0.5, ordinal = 5),
            // without the first one
            @Constant(doubleValue = 1.0, ordinal = 1),
            @Constant(doubleValue = 1.0, ordinal = 2),
            @Constant(doubleValue = 1.0, ordinal = 3),
            @Constant(doubleValue = 1.0, ordinal = 4),
            @Constant(doubleValue = 1.0, ordinal = 5),
    })
    double onLivingUpdate(double constant) {
        return constant * getSizeCM();
    }
}


<|start_filename|>src/main/resources/assets/chiseled_me/advancements/concentrated.json<|end_filename|>
{
  "parent": "chiseled_me:advancements",
  "display": {
    "icon": {
      "item": "chiseled_me:pym_essence_x"
    },
    "title": {
      "translate": "advancement.chiseled_me:concentrated"
    },
    "description": {
      "translate": "advancement.chiseled_me:concentrated.desc"
    },
    "anonunce_to_chat": false
  },
  "criteria": {
    "stuff": {
      "trigger": "minecraft:inventory_changed",
      "conditions": {
        "items": [
          {
            "item": "chiseled_me:pym_essence_x"
          }
        ]
      }
    }
  },
  "rewards": {
    "recipes": [
      "chiseled_me:blue_star",
      "chiseled_me:recalibrator_reduction_9",
      "chiseled_me:recalibrator_reduction_10",
      "chiseled_me:recalibrator_reduction_11",
      "chiseled_me:recalibrator_reduction_12"
    ]
  }
}


<|start_filename|>src/main/java/dev/necauqua/mods/cm/mixin/PlayerInteractionManagerMixin.java<|end_filename|>
package dev.necauqua.mods.cm.mixin;

import dev.necauqua.mods.cm.api.ISized;
import dev.necauqua.mods.cm.api.IWorldPreciseEvents;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.server.management.PlayerInteractionManager;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;

@Mixin(PlayerInteractionManager.class)
public final class PlayerInteractionManagerMixin {

    @Redirect(method = "tryHarvestBlock", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/World;playEvent(Lnet/minecraft/entity/player/EntityPlayer;ILnet/minecraft/util/math/BlockPos;I)V"))
    void playSound(World self, EntityPlayer player, int type, BlockPos pos, int data) {
        double size = ((ISized) player).getSizeCM();
        ((IWorldPreciseEvents) self).playEvent(player, type, pos, data, size, player.getPositionEyes(1.0f));
    }
}


<|start_filename|>src/main/java/dev/necauqua/mods/cm/mixin/entity/EntityTrackerEntryMixin.java<|end_filename|>
/*
 * Copyright (c) 2017-2021 <NAME> <<EMAIL>>
 * Licensed under MIT, see the LICENSE file for details.
 */

package dev.necauqua.mods.cm.mixin.entity;

import dev.necauqua.mods.cm.api.ISized;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityTrackerEntry;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Constant;
import org.spongepowered.asm.mixin.injection.ModifyConstant;
import org.spongepowered.asm.mixin.injection.ModifyVariable;

@Mixin(EntityTrackerEntry.class)
public final class EntityTrackerEntryMixin {

    @ModifyVariable(method = "isVisibleTo", at = @At("STORE"))
    int isVisibleTo(int i) {
        double size = ((ISized) trackedEntity).getSizeCM();
        return size > 1.0 ? (int) (i * size) : i;
    }

    @ModifyConstant(method = "updatePlayerList", constant = @Constant(doubleValue = 16.0))
    double updatePlayerList(double constant) {
        double size = ((ISized) trackedEntity).getSizeCM();
        return constant * size * size;
    }

    @ModifyConstant(method = "updatePlayerList", constant = @Constant(longValue = 128))
    long updatePlayerList(long constant) {
        double size = ((ISized) trackedEntity).getSizeCM();
        return (long) (constant * size * size);
    }

    @Shadow
    @Final
    private Entity trackedEntity;
}


<|start_filename|>src/main/java/dev/necauqua/mods/cm/advancements/AdvancementTriggers.java<|end_filename|>
/*
 * Copyright (c) 2017-2021 <NAME> <<EMAIL>>
 * Licensed under MIT, see the LICENSE file for details.
 */

package dev.necauqua.mods.cm.advancements;

import dev.necauqua.mods.cm.ChiseledMe.Init;
import net.minecraft.advancements.CriteriaTriggers;

import static dev.necauqua.mods.cm.ChiseledMe.ns;

public final class AdvancementTriggers {
    public static final StatelessTrigger RECALIBRATOR_RESET = new StatelessTrigger(ns("recalibrator_reset"));
    public static final StatelessTrigger BLUE_STAR_DECRAFT = new StatelessTrigger(ns("blue_star_decraft"));
    public static final StatelessTrigger WEIRD_BEACON_CRAFT = new StatelessTrigger(ns("weird_beacon_craft"));

    @Init
    private static void init() {
        CriteriaTriggers.register(RECALIBRATOR_RESET);
        CriteriaTriggers.register(BLUE_STAR_DECRAFT);
        CriteriaTriggers.register(WEIRD_BEACON_CRAFT);
    }
}


<|start_filename|>src/main/java/dev/necauqua/mods/cm/mixin/entity/projectile/EntityStatusUpdateParticlesMixin.java<|end_filename|>
/*
 * Copyright (c) 2017-2021 <NAME> <<EMAIL>>
 * Licensed under MIT, see the LICENSE file for details.
 */

package dev.necauqua.mods.cm.mixin.entity.projectile;

import dev.necauqua.mods.cm.mixin.entity.EntityMixin;
import dev.necauqua.mods.cm.size.EntitySizeInteractions;
import net.minecraft.entity.projectile.EntitySnowball;
import net.minecraft.entity.projectile.EntityTippedArrow;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.ModifyArg;

@Mixin({EntitySnowball.class, EntityTippedArrow.class})
public abstract class EntityStatusUpdateParticlesMixin extends EntityMixin {

    @ModifyArg(method = "handleStatusUpdate", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/World;spawnParticle(Lnet/minecraft/util/EnumParticleTypes;DDDDDD[I)V"))
    int[] handleStatusUpdate(int[] args) {
        return EntitySizeInteractions.appendSize(args, $cm$size);
    }
}


<|start_filename|>src/main/resources/assets/chiseled_me/models/item/recalibrator_reset.json<|end_filename|>
{
  "parent": "item/generated",
  "textures": {
    "layer0": "chiseled_me:items/recalibrator_reset"
  }
}


<|start_filename|>src/main/resources/chiseled-me.compat.mixins.json<|end_filename|>
{
  "required": true,
  "minVersion": "0.8",
  "package": "dev.necauqua.mods.cm.mixin.compat",
  "refmap": "chiseled-me.mixins.refmap.json",
  "compatibilityLevel": "JAVA_8",
  "mixins": [
    "CustomNpcFixMixin",
    "CustomNpcFixMixin$EntityCustomNpcHitboxFix",
    "CustomNpcFixMixin$EntityNpcHitboxFixes",
    "MetamorphMixin",
    "MetamorphMixin$EntityMorphMixin"
  ],
  "client": [
    "CustomNpcFixMixin$GuiContainerNPCInterfaceMixin",
    "CustomNpcFixMixin$GuiNPCInterfaceMixin",
    "CustomNpcFixMixin$RenderFixes",
    "GCEntityClientPlayerMPMixin",
    "NeatMixin",
    "Rfp2DummyPlayerMixin"
  ],
  "injectors": {
    "defaultRequire": 1
  }
}


<|start_filename|>src/main/java/dev/necauqua/mods/cm/recipes/PotionIngredientFactory.java<|end_filename|>
/*
 * Copyright (c) 2017-2021 <NAME> <<EMAIL>>
 * Licensed under MIT, see the LICENSE file for details.
 */

package dev.necauqua.mods.cm.recipes;

import com.google.gson.JsonObject;
import com.google.gson.JsonSyntaxException;
import net.minecraft.advancements.critereon.EnchantmentPredicate;
import net.minecraft.advancements.critereon.ItemPredicate;
import net.minecraft.advancements.critereon.MinMaxBounds;
import net.minecraft.advancements.critereon.NBTPredicate;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.item.crafting.IRecipe;
import net.minecraft.item.crafting.Ingredient;
import net.minecraft.potion.PotionType;
import net.minecraft.potion.PotionUtils;
import net.minecraft.util.JsonUtils;
import net.minecraftforge.advancements.critereon.ItemPredicates;
import net.minecraftforge.common.crafting.IIngredientFactory;
import net.minecraftforge.common.crafting.JsonContext;
import net.minecraftforge.event.RegistryEvent;
import net.minecraftforge.fml.common.Mod.EventBusSubscriber;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.HashMap;
import java.util.Map;

import static dev.necauqua.mods.cm.ChiseledMe.MODID;
import static dev.necauqua.mods.cm.ChiseledMe.ns;
import static net.minecraft.init.Items.*;

@EventBusSubscriber(modid = MODID)
public final class PotionIngredientFactory implements IIngredientFactory {

    private static final Map<String, Item> potionTypes = new HashMap<>();

    static {
        potionTypes.put("regular", POTIONITEM);
        potionTypes.put("splash", SPLASH_POTION);
        potionTypes.put("lingering", LINGERING_POTION);
    }

    private static Item getPotionItem(JsonObject json) {
        String potionType = JsonUtils.getString(json, "potion_type", "regular");
        Item item = potionTypes.get(potionType);
        if (item == null) {
            throw new JsonSyntaxException("Expected potion_type to be one of regular, splash or lingering, was " + potionType);
        }
        return item;
    }

    private static PotionType getPotion(@Nullable JsonContext context, JsonObject json) {
        String s = JsonUtils.getString(json, "potion");
        String potionName = context == null ? s : context.appendModId(s);
        PotionType potion = PotionType.getPotionTypeForName(potionName);
        if (potion == null) {
            throw new JsonSyntaxException("No potion registered for name " + potionName);
        }
        return potion;
    }

    @Override
    @Nonnull
    public Ingredient parse(JsonContext context, JsonObject json) {
        Item item = getPotionItem(json);
        PotionType potion = getPotion(context, json);
        return new Ingredient(PotionUtils.addPotionToItemStack(new ItemStack(item), potion)) {
            @Override
            public boolean apply(@Nullable ItemStack stack) {
                return stack != null && stack.getItem() == item && PotionUtils.getPotionFromItem(stack) == potion;
            }
        };
    }

    @SubscribeEvent
    public static void itemPredicateHack(RegistryEvent.Register<IRecipe> e) {
        ItemPredicates.register(ns("potion"), json -> {
            Item item = getPotionItem(json);
            PotionType potion = getPotion(null, json);
            return new ItemPredicate(
                    item,
                    null,
                    MinMaxBounds.UNBOUNDED,
                    MinMaxBounds.UNBOUNDED,
                    new EnchantmentPredicate[0],
                    potion,
                    NBTPredicate.ANY);
        });
    }
}


<|start_filename|>src/main/java/dev/necauqua/mods/cm/mixin/entity/ai/EntityAIMateMixin.java<|end_filename|>
/*
 * Copyright (c) 2017-2021 <NAME> <<EMAIL>>
 * Licensed under MIT, see the LICENSE file for details.
 */

package dev.necauqua.mods.cm.mixin.entity.ai;

import dev.necauqua.mods.cm.api.ISized;
import net.minecraft.entity.ai.EntityAIMate;
import net.minecraft.entity.passive.EntityAnimal;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Constant;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.ModifyConstant;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;

import javax.annotation.Nullable;

// disallow mating between differently sized entities
@Mixin(EntityAIMate.class)
public final class EntityAIMateMixin {

    @Inject(method = "shouldExecute", at = @At("HEAD"), cancellable = true)
    void shouldExecute(CallbackInfoReturnable<Boolean> cir) {
        if (targetMate != null && ((ISized) animal).getSizeCM() != ((ISized) targetMate).getSizeCM()) {
            cir.setReturnValue(false);
        }
    }

    @Inject(method = "shouldContinueExecuting", at = @At("HEAD"), cancellable = true)
    void shouldContinueExecuting(CallbackInfoReturnable<Boolean> cir) {
        if (targetMate != null && ((ISized) animal).getSizeCM() != ((ISized) targetMate).getSizeCM()) {
            cir.setReturnValue(false);
        }
    }

    @ModifyConstant(method = "getNearbyMate", constant = @Constant(doubleValue = 8.0))
    double getNearbyMate(double constant) {
        return constant * ((ISized) animal).getSizeCM();
    }

    @Shadow
    @Final
    private EntityAnimal animal;

    @Shadow
    @Nullable
    private EntityAnimal targetMate;
}


<|start_filename|>src/main/java/dev/necauqua/mods/cm/mixin/entity/EntitySlimeMixin.java<|end_filename|>
/*
 * Copyright (c) 2017-2021 <NAME> <<EMAIL>>
 * Licensed under MIT, see the LICENSE file for details.
 */

package dev.necauqua.mods.cm.mixin.entity;

import dev.necauqua.mods.cm.size.EntitySizeInteractions;
import net.minecraft.entity.monster.EntitySlime;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.ModifyArg;
import org.spongepowered.asm.mixin.injection.ModifyVariable;

@Mixin(EntitySlime.class)
public abstract class EntitySlimeMixin extends EntityMixin {

    @ModifyVariable(method = "onUpdate", ordinal = 1, at = @At("STORE"))
    float spawnParticles(float f1) {
        return (float) (f1 * $cm$size);
    }

    @ModifyArg(method = "onUpdate", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/World;spawnParticle(Lnet/minecraft/util/EnumParticleTypes;DDDDDD[I)V"))
    int[] spawnParticles(int[] args) {
        return EntitySizeInteractions.appendSize(args, $cm$size);
    }
}


<|start_filename|>src/main/java/dev/necauqua/mods/cm/recipes/SupersmallsEnabledCondition.java<|end_filename|>
/*
 * Copyright (c) 2017-2021 <NAME> <<EMAIL>>
 * Licensed under MIT, see the LICENSE file for details.
 */

package dev.necauqua.mods.cm.recipes;

import com.google.gson.JsonObject;
import dev.necauqua.mods.cm.Config;
import net.minecraftforge.common.crafting.IConditionFactory;
import net.minecraftforge.common.crafting.JsonContext;

import java.util.function.BooleanSupplier;

@SuppressWarnings("unused") // referred from json
public final class SupersmallsEnabledCondition implements IConditionFactory {

    @Override
    public BooleanSupplier parse(JsonContext context, JsonObject json) {
        return () -> Config.enableSupersmalls;
    }
}


<|start_filename|>src/main/java/dev/necauqua/mods/cm/mixin/client/ParticleMixin.java<|end_filename|>
/*
 * Copyright (c) 2017-2021 <NAME> <<EMAIL>>
 * Licensed under MIT, see the LICENSE file for details.
 */

package dev.necauqua.mods.cm.mixin.client;

import dev.necauqua.mods.cm.api.IRenderSized;
import dev.necauqua.mods.cm.api.ISized;
import dev.necauqua.mods.cm.size.EntitySizeInteractions;
import net.minecraft.client.particle.*;
import net.minecraft.entity.Entity;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.world.World;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.*;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;

import static org.objectweb.asm.Opcodes.PUTFIELD;
import static org.spongepowered.asm.mixin.injection.At.Shift.AFTER;

@Mixin(Particle.class)
public abstract class ParticleMixin implements ISized {

    public double $cm$size = 1.0;
    private float $cm$original_width;
    private float $cm$original_height;

    @Override
    public double getSizeCM() {
        return $cm$size;
    }

    @Override
    public void setSizeCM(double size) {
        $cm$size = size;

        width = (float) ($cm$original_width * size);
        height = (float) ($cm$original_height * size);

        float w = width / 2.0F;
        setBoundingBox(new AxisAlignedBB(posX - w, posY, posZ - w, posX + w, posY + height, posZ + w));
    }

    @ModifyVariable(method = "setSize", at = @At("HEAD"), ordinal = 0)
    float setSizeWidth(float width) {
        $cm$original_width = width;
        return (float) (width * $cm$size);
    }

    @ModifyVariable(method = "setSize", at = @At("HEAD"), ordinal = 1)
    float setSizeHeight(float height) {
        $cm$original_height = height;
        return (float) (height * $cm$size);
    }

    @ModifyVariable(method = "move", at = @At("HEAD"), ordinal = 0)
    double moveX(double x) {
        return x * $cm$size;
    }

    @ModifyVariable(method = "move", at = @At("HEAD"), ordinal = 1)
    double moveY(double y) {
        return y * $cm$size;
    }

    @ModifyVariable(method = "move", at = @At("HEAD"), ordinal = 2)
    double moveZ(double z) {
        return z * $cm$size;
    }

    @ModifyVariable(method = "renderParticle", ordinal = 10, at = @At("STORE"))
    float renderParticle(float scale) {
        return (float) (scale * $cm$size);
    }

    @Shadow
    protected float width;
    @Shadow
    protected float height;

    @Shadow
    protected double posX;
    @Shadow
    protected double posY;
    @Shadow
    protected double posZ;

    @Shadow
    protected double motionX;
    @Shadow
    protected double motionY;
    @Shadow
    protected double motionZ;

    @Shadow
    protected int particleAge;

    @Shadow
    protected int particleMaxAge;

    @Shadow
    public abstract void setBoundingBox(AxisAlignedBB bb);

    @Mixin(ParticleCrit.class)
    public static abstract class ParticleCritMixin extends ParticleMixin implements ISized {

        // this one onUpdate in constructor runs while the size is still
        // not set (so its 1) and for one tick the particle moves as if not scaled
        @Redirect(method = "<init>(Lnet/minecraft/world/World;DDDDDDF)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/particle/ParticleCrit;onUpdate()V"))
        void removeExtraOnUpdate(ParticleCrit self) {
        }
    }

    @Mixin(ParticlePortal.class)
    public static abstract class ParticlePortalMixin extends ParticleMixin {

        @ModifyVariable(method = "move", at = @At("HEAD"), ordinal = 0)
        double moveX(double x) {
            return x * $cm$size;
        }

        @ModifyVariable(method = "move", at = @At("HEAD"), ordinal = 1)
        double moveY(double y) {
            return y * $cm$size;
        }

        @ModifyVariable(method = "move", at = @At("HEAD"), ordinal = 2)
        double moveZ(double z) {
            return z * $cm$size;
        }

        @Redirect(method = "onUpdate", at = @At(value = "FIELD", target = "Lnet/minecraft/client/particle/ParticlePortal;motionX:D"))
        double onUpdateMotionX(ParticlePortal self) {
            return motionX * $cm$size;
        }

        @Inject(method = "onUpdate", at = @At(value = "FIELD", opcode = PUTFIELD, shift = AFTER, target = "Lnet/minecraft/client/particle/ParticlePortal;posY:D"))
        void onUpdateMotionY(CallbackInfo ci) {
            float f = (float) particleAge / (float) particleMaxAge;
            float f1 = -f + f * f * 2.0f;
            float f2 = 1.0f - f1;
            posY = portalPosY + (motionY * f2 + (1.0f - f)) * $cm$size;
        }

        @Redirect(method = "onUpdate", at = @At(value = "FIELD", target = "Lnet/minecraft/client/particle/ParticlePortal;motionZ:D"))
        double onUpdateMotionZ(ParticlePortal self) {
            return motionZ * $cm$size;
        }

        @Shadow
        @Final
        private double portalPosY;
    }

    @Mixin(ParticleDigging.class)
    public static abstract class ParticleDiggingMixin extends ParticleMixin {

        @ModifyVariable(method = "renderParticle", ordinal = 10, at = @At("STORE"))
        float renderParticle(float scale) {
            return (float) (scale * $cm$size);
        }
    }

    @Mixin(ParticleBreaking.class)
    public static abstract class ParticleBreakingMixin extends ParticleMixin {

        @ModifyVariable(method = "renderParticle", ordinal = 10, at = @At("STORE"))
        float renderParticle(float scale) {
            return (float) (scale * $cm$size);
        }
    }

    @Mixin(ParticleBreaking.Factory.class)
    public static abstract class ParticleBreakingFactoryMixin {

        @ModifyConstant(method = "createParticle", constant = @Constant(intValue = 1, ordinal = 0))
        int createParticle(int constant, int particleID, World worldIn, double xCoordIn, double yCoordIn, double zCoordIn, double xSpeedIn, double ySpeedIn, double zSpeedIn, int... parameters) {
            return parameters.length == 3 ?
                    Integer.MAX_VALUE : // meh
                    constant;
        }
    }

    @Mixin(ParticleSweepAttack.class)
    public static abstract class ParticleSweepAttackMixin extends ParticleMixin {

        @ModifyVariable(method = "renderParticle", ordinal = 10, at = @At("STORE"))
        float renderParticle(float scale) {
            return (float) (scale * $cm$size);
        }
    }

    @Mixin(ParticleEmitter.class)
    public static abstract class ParticleEmitterMixin extends ParticleMixin {

        @ModifyArg(method = "onUpdate", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/World;spawnParticle(Lnet/minecraft/util/EnumParticleTypes;ZDDDDDD[I)V"))
        int[] spawnParticleArgs(int[] args) {
            return EntitySizeInteractions.appendSize(args, ((IRenderSized) attachedEntity).getSizeCM());
        }

        @Shadow
        @Final
        private Entity attachedEntity;
    }

    @Mixin(ParticleItemPickup.class)
    public static abstract class ParticleItemPickupMixin extends ParticleMixin {

        @ModifyVariable(method = "<init>", at = @At("LOAD"))
        float constructor(float yOffset, World world, Entity item, Entity thrower) {
            return (float) (yOffset * ((IRenderSized) thrower).getSizeCM());
        }
    }
}


<|start_filename|>src/main/java/dev/necauqua/mods/cm/mixin/entity/ai/EntityAITemptMixin.java<|end_filename|>
/*
 * Copyright (c) 2017-2021 <NAME> <<EMAIL>>
 * Licensed under MIT, see the LICENSE file for details.
 */

package dev.necauqua.mods.cm.mixin.entity.ai;

import dev.necauqua.mods.cm.api.ISized;
import net.minecraft.entity.EntityCreature;
import net.minecraft.entity.ai.EntityAITempt;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.Constant;
import org.spongepowered.asm.mixin.injection.ModifyConstant;

// scale area and closest distance for entities that follow you when you hold food
@Mixin(EntityAITempt.class)
public final class EntityAITemptMixin {

    @ModifyConstant(method = "shouldExecute", constant = @Constant(doubleValue = 10.0))
    double shouldExecute(double constant) {
        return Math.max(0.5625, constant * ((ISized) temptedEntity).getSizeCM());
        // ^ limit it to 9 pixels minimum because mob AI works in integer block coordinates, meh
    }

    @ModifyConstant(method = "shouldContinueExecuting", constant = @Constant(doubleValue = 0.010000000000000002))
    double shouldContinueExecuting(double constant) {
        double size = ((ISized) temptedEntity).getSizeCM();
        return constant * size * size;
    }

    @ModifyConstant(method = "shouldContinueExecuting", constant = @Constant(doubleValue = 36.0))
    double shouldContinueExecutingLimited(double constant) {
        double size = ((ISized) temptedEntity).getSizeCM();
        return Math.max(0.5625 * 0.5625, constant * size * size);
        // ^ limit it to 9 pixels minimum because mob AI works in integer block coordinates, meh
    }

    @ModifyConstant(method = "updateTask", constant = @Constant(doubleValue = 6.25))
    double updateTask(double constant) {
        double size = ((ISized) temptedEntity).getSizeCM();
        return Math.max(0.5625 * 0.5625, constant * size * size);
    }

    @Shadow
    @Final
    private EntityCreature temptedEntity;
}


<|start_filename|>src/main/java/dev/necauqua/mods/cm/Network.java<|end_filename|>
/*
 * Copyright (c) 2017-2021 <NAME> <<EMAIL>>
 * Licensed under MIT, see the LICENSE file for details.
 */

package dev.necauqua.mods.cm;

import dev.necauqua.mods.cm.ChiseledMe.Init;
import dev.necauqua.mods.cm.api.IRenderSized;
import io.netty.buffer.Unpooled;
import net.minecraft.entity.Entity;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.network.PacketBuffer;
import net.minecraft.world.World;
import net.minecraft.world.WorldServer;
import net.minecraftforge.fml.common.Mod.EventBusSubscriber;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.common.network.FMLEventChannel;
import net.minecraftforge.fml.common.network.FMLNetworkEvent.ClientCustomPacketEvent;
import net.minecraftforge.fml.common.network.NetworkRegistry;
import net.minecraftforge.fml.common.network.internal.FMLProxyPacket;

import java.util.function.Consumer;

import static dev.necauqua.mods.cm.ChiseledMe.MODID;

@EventBusSubscriber(modid = MODID)
public final class Network {
    private static final FMLEventChannel channel = NetworkRegistry.INSTANCE.newEventDrivenChannel(MODID);

    private Network() {}

    @Init
    private static void init() {
        channel.register(Network.class);
    }

    @SubscribeEvent
    public static void onClientReceive(ClientCustomPacketEvent e) {
        PacketBuffer payload = new PacketBuffer(e.getPacket().payload());
        byte id = payload.readByte();
        switch (id) {
            case 0: {
                double size = payload.readDouble();
                int lerpTime = payload.readInt();
                SidedHandler.instance.scheduleClientMainLoopTask(() -> {
                    EntityPlayer player = SidedHandler.instance.getClientPlayer();
                    if (player != null) {
                        ((IRenderSized) player).setSizeCM(size, lerpTime);
                    }
                });
                break;
            }
            case 1: {
                int entityId = payload.readInt();
                double size = payload.readDouble();
                int lerpTime = payload.readInt();
                SidedHandler.instance.scheduleClientMainLoopTask(() -> {
                    World world = SidedHandler.instance.getClientWorld();
                    if (world == null) {
                        return;
                    }
                    Entity entity = world.getEntityByID(entityId);
                    if (entity != null) {
                        ((IRenderSized) entity).setSizeCM(size, lerpTime);
                    }
                });
                break;
            }
            default:
                invalidPacket(id, payload);
        }
    }

    private static FMLProxyPacket packet(int id, Consumer<PacketBuffer> data) {
        PacketBuffer payload = new PacketBuffer(Unpooled.buffer());
        payload.writeByte(id);
        data.accept(payload);
        return new FMLProxyPacket(payload, MODID);
    }

    private static void invalidPacket(byte id, PacketBuffer payload) {
        StringBuilder out = new StringBuilder("Invalid packet received, its content was: ").append(id);
        int i = -1;
        while (payload.isReadable() && i++ < 16) {
            byte b = payload.readByte();
            String hex = Integer.toHexString(b);
            out.append(", ");
            if (hex.length() < 2) {
                out.append('0');
            }
            out.append(hex);
        }
        if (payload.isReadable()) {
            out.append("and ").append(payload.readableBytes()).append(" bytes more...");
        }
        Log.error(out);
    }

    public static void sync(Entity entity, double size, int lerpTime) {
        if (entity instanceof EntityPlayerMP) {
            channel.sendTo(packet(0, p -> {
                p.writeDouble(size);
                p.writeInt(lerpTime);
            }), (EntityPlayerMP) entity);
        }
        for (EntityPlayer entityPlayer : ((WorldServer) entity.world).getEntityTracker().getTrackingPlayers(entity)) {
            channel.sendTo(packet(1, p -> {
                p.writeInt(entity.getEntityId());
                p.writeDouble(size);
                p.writeInt(lerpTime);
            }), (EntityPlayerMP) entityPlayer);
        }
    }
}
