blusunrize.immersiveengineering.api.shader.ShaderRegistry.java Source code

Java tutorial

Introduction

Here is the source code for blusunrize.immersiveengineering.api.shader.ShaderRegistry.java

Source

/*
 * BluSunrize
 * Copyright (c) 2017
 *
 * This code is licensed under "Blu's License of Common Sense"
 * Details can be found in the license file in the root folder of this project
 */

package blusunrize.immersiveengineering.api.shader;

import blusunrize.immersiveengineering.api.Lib;
import blusunrize.immersiveengineering.api.ManualHelper;
import blusunrize.immersiveengineering.api.crafting.IngredientStack;
import blusunrize.immersiveengineering.api.shader.ShaderCase.ShaderLayer;
import blusunrize.lib.manual.ManualInstance.ManualEntry;
import blusunrize.lib.manual.ManualPages;
import blusunrize.lib.manual.ManualPages.PositionedItemStack;
import com.google.common.collect.ArrayListMultimap;
import net.minecraft.item.EnumRarity;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.util.NonNullList;
import net.minecraft.util.ResourceLocation;
import org.apache.commons.lang3.tuple.Triple;

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

public class ShaderRegistry {
    /**
     * A map of shader name to ShaderRegistryEntry, which contains ShaderCases, rarity, weight and loot specifics
     */
    public static LinkedHashMap<String, ShaderRegistryEntry> shaderRegistry = new LinkedHashMap<String, ShaderRegistryEntry>();

    /**
     * A list of shader names that can generate in chests/crates. Names are added multiple times depending on their weight
     */
    public static ArrayList<String> chestLootShaders = new ArrayList<String>();

    /**
     * A map of EnumRarities to weight for grab bag distribution
     */
    public static HashMap<EnumRarity, Integer> rarityWeightMap = new HashMap<EnumRarity, Integer>();

    static {
        rarityWeightMap.put(EnumRarity.COMMON, 9);
        rarityWeightMap.put(EnumRarity.UNCOMMON, 7);
        rarityWeightMap.put(EnumRarity.RARE, 5);
        rarityWeightMap.put(EnumRarity.EPIC, 3);
        rarityWeightMap.put(Lib.RARITY_Masterwork, 1);
    }

    /**
     * A list of EnumRarities sorted by their weight
     */
    public static ArrayList<EnumRarity> sortedRarityMap = new ArrayList<EnumRarity>();

    /**
     * A map of player names to received shaders. Saved with worlddata. Designed to prioritize shaders the player has not yet received
     */
    public static ArrayListMultimap<String, String> receivedShaders = ArrayListMultimap.create();
    /**
     * The map of EnumRarities to the total weight of all shaders of that rarity or rarer
     */
    public static HashMap<EnumRarity, Integer> totalWeight = new HashMap<EnumRarity, Integer>();
    /**
     * The total weight in relation to the player. This takes into account shaders the player has gotten, which then result in less weight
     */
    public static HashMap<String, HashMap<EnumRarity, Integer>> playerTotalWeight = new HashMap<String, HashMap<EnumRarity, Integer>>();
    /**
     * The deafault cost for replicating a shader. Prices are multiplied with 10-rarity level. Prices can be adjusted for every registry entry
     */
    public static IngredientStack defaultReplicationCost = new IngredientStack("dustSilver");
    /**
     * A HashMap to set default texture bounds for the additional layers of a shadercase. Saves you the trouble of redfining them for every shader. See {@link ShaderLayer#setTextureBounds(double... bounds)}.
     */
    public static HashMap<ResourceLocation, double[]> defaultLayerBounds = new HashMap<ResourceLocation, double[]>();

    public static ShaderCase getShader(String name, String shaderType) {
        if (shaderRegistry.containsKey(name))
            return shaderRegistry.get(name).getCase(shaderType);
        return null;
    }

    public static ShaderRegistryEntry registerShader(String name, String overlayType, EnumRarity rarity,
            int colourPrimary, int colourSecondary, int colourBackground, int colourBlade, String additionalTexture,
            int colourAdditional, boolean loot, boolean bags) {
        registerShader_Item(name, rarity, colourBackground, colourPrimary, colourSecondary);
        registerShader_Revolver(name, overlayType, rarity, colourBackground, colourPrimary, colourSecondary,
                colourBlade, additionalTexture, colourAdditional);
        registerShader_Chemthrower(name, overlayType, rarity, colourBackground, colourPrimary, colourSecondary,
                additionalTexture, colourAdditional);
        registerShader_Drill(name, overlayType, rarity, colourBackground, colourPrimary, colourSecondary,
                additionalTexture, colourAdditional);
        registerShader_Railgun(name, overlayType, rarity, colourBackground, colourPrimary, colourSecondary,
                additionalTexture, colourAdditional);
        registerShader_Shield(name, overlayType, rarity, colourPrimary, colourSecondary, additionalTexture,
                colourAdditional);
        registerShader_Minecart(name, overlayType, rarity, colourPrimary, colourSecondary, additionalTexture,
                colourAdditional);
        registerShader_Balloon(name, overlayType, rarity, colourPrimary, colourSecondary, additionalTexture,
                colourAdditional);
        registerShader_Banner(name, overlayType, rarity, colourPrimary, colourSecondary, additionalTexture,
                colourAdditional);
        for (IShaderRegistryMethod method : shaderRegistrationMethods)
            method.apply(name, overlayType, rarity, colourBackground, colourPrimary, colourSecondary, colourBlade,
                    additionalTexture, colourAdditional);
        return shaderRegistry.get(name).setCrateLoot(loot).setBagLoot(bags).setReplicationCost(
                defaultReplicationCost.copyWithMultipliedSize(10 - rarityWeightMap.get(rarity)));
    }

    public static <T extends ShaderCase> T registerShaderCase(String name, T shader, EnumRarity rarity) {
        if (!shaderRegistry.containsKey(name))
            shaderRegistry.put(name, new ShaderRegistryEntry(name, rarity, shader));
        else
            shaderRegistry.get(name).addCase(shader);
        return shader;
    }

    /**
     * Method to register a default implementation of Item Shaders<br>
     * It is used for the colour and layers of the base shader item
     *
     * @param name    name of the shader
     * @param rarity  Rarity of the shader item
     * @param colour0 grip colour
     * @param colour1 base colour
     * @param colour2 design colour
     * @return the registered ShaderCase
     */
    public static ShaderCaseItem registerShader_Item(String name, EnumRarity rarity, int colour0, int colour1,
            int colour2) {
        ArrayList<ShaderLayer> list = new ArrayList();
        list.add(new ShaderLayer(new ResourceLocation("immersiveengineering:items/shader_0"), colour0));
        list.add(new ShaderLayer(new ResourceLocation("immersiveengineering:items/shader_1"), colour1));
        list.add(new ShaderLayer(new ResourceLocation("immersiveengineering:items/shader_2"), colour2));
        ShaderCaseItem shader = new ShaderCaseItem(list);
        return registerShaderCase(name, shader, rarity);
    }

    /**
     * Method to register a default implementation of Chemthrower Shaders<br>
     *
     * @param name              name of the shader
     * @param overlayType       uses IE's existing overlays. To use custom ones, you'll need your own method.
     * @param rarity            Rarity of the shader item
     * @param colour0           grip colour
     * @param colour1           base colour
     * @param colour2           design colour
     * @param colourBlade       colour of the bayonet blade
     * @param additionalTexture additional overlay texture. Null if not needed.
     * @param colourAddtional   colour for the additional texture, if present
     * @return the registered ShaderCase
     */
    public static ShaderCaseRevolver registerShader_Revolver(String name, String overlayType, EnumRarity rarity,
            int colour0, int colour1, int colour2, int colourBlade, String additionalTexture, int colourAddtional) {
        ArrayList<ShaderLayer> list = new ArrayList();
        list.add(new ShaderLayer(new ResourceLocation("immersiveengineering:revolvers/shaders/revolver_grip"),
                colour0));
        list.add(new ShaderLayer(new ResourceLocation("immersiveengineering:revolvers/shaders/revolver_0"),
                colour1));
        list.add(new ShaderLayer(new ResourceLocation("immersiveengineering:revolvers/shaders/revolver_0"),
                colourBlade));
        list.add(new ShaderLayer(
                new ResourceLocation("immersiveengineering:revolvers/shaders/revolver_1_" + overlayType), colour2));
        if (additionalTexture != null) {
            ResourceLocation rl = additionalTexture.indexOf(58) >= 0 ? new ResourceLocation(additionalTexture)
                    : new ResourceLocation("immersiveengineering:revolvers/shaders/revolver_" + additionalTexture);
            list.add(new ShaderLayer(rl, colourAddtional));
        }
        list.add(new ShaderLayer(new ResourceLocation("immersiveengineering:revolvers/shaders/revolver_uncoloured"),
                0xffffffff));
        ShaderCaseRevolver shader = new ShaderCaseRevolver(list);
        return registerShaderCase(name, shader, rarity);
    }

    /**
     * Method to register a default implementation of Chemthrower Shaders<br>
     *
     * @param name              name of the shader
     * @param overlayType       uses IE's existing overlays. To use custom ones, you'll need your own method.
     * @param rarity            Rarity of the shader item
     * @param colour0           grip colour
     * @param colour1           base colour
     * @param colour2           design colour
     * @param additionalTexture additional overlay texture. Null if not needed.
     * @param colourAddtional   colour for the additional texture, if present
     * @return the registered ShaderCase
     */
    public static ShaderCaseChemthrower registerShader_Chemthrower(String name, String overlayType,
            EnumRarity rarity, int colour0, int colour1, int colour2, String additionalTexture,
            int colourAddtional) {
        ArrayList<ShaderLayer> list = new ArrayList();
        list.add(
                new ShaderLayer(new ResourceLocation("immersiveengineering:items/shaders/chemthrower_0"), colour0));
        list.add(
                new ShaderLayer(new ResourceLocation("immersiveengineering:items/shaders/chemthrower_0"), colour1));
        list.add(new ShaderLayer(
                new ResourceLocation("immersiveengineering:items/shaders/chemthrower_1_" + overlayType), colour2));
        if (additionalTexture != null) {
            ResourceLocation rl = additionalTexture.indexOf(58) >= 0 ? new ResourceLocation(additionalTexture)
                    : new ResourceLocation("immersiveengineering:items/shaders/chemthrower_" + additionalTexture);
            list.add(new ShaderLayer(rl, colourAddtional));
        }
        list.add(new ShaderLayer(new ResourceLocation("immersiveengineering:items/shaders/chemthrower_uncoloured"),
                0xffffffff));
        ShaderCaseChemthrower shader = new ShaderCaseChemthrower(list);
        return registerShaderCase(name, shader, rarity);
    }

    /**
     * Method to register a default implementation of Drill Shaders<br>
     * Note that they have an extra layer with null for the ResourceLocation, for the drill head and augers
     *
     * @param name              name of the shader
     * @param overlayType       uses IE's existing overlays. To use custom ones, you'll need your own method.
     * @param rarity            Rarity of the shader item
     * @param colour0           grip colour
     * @param colour1           base colour
     * @param colour2           design colour
     * @param additionalTexture additional overlay texture. Null if not needed.
     * @param colourAddtional   colour for the additional texture, if present
     * @return the registered ShaderCase
     */
    public static ShaderCaseDrill registerShader_Drill(String name, String overlayType, EnumRarity rarity,
            int colour0, int colour1, int colour2, String additionalTexture, int colourAddtional) {
        ArrayList<ShaderLayer> list = new ArrayList();
        list.add(new ShaderLayer(new ResourceLocation("immersiveengineering:items/shaders/drill_diesel_0"),
                colour0));
        list.add(new ShaderLayer(new ResourceLocation("immersiveengineering:items/shaders/drill_diesel_0"),
                colour1));
        list.add(new ShaderLayer(
                new ResourceLocation("immersiveengineering:items/shaders/drill_diesel_1_" + overlayType), colour2));
        if (additionalTexture != null) {
            ResourceLocation rl = additionalTexture.indexOf(58) >= 0 ? new ResourceLocation(additionalTexture)
                    : new ResourceLocation("immersiveengineering:items/shaders/drill_diesel_" + additionalTexture);
            list.add(new ShaderLayer(rl, colourAddtional));
        }
        list.add(new ShaderLayer(new ResourceLocation("immersiveengineering:items/shaders/drill_diesel_uncoloured"),
                0xffffffff));
        list.add(new ShaderLayer(null, 0xffffffff));//final pass is for drill head and augers
        ShaderCaseDrill shader = new ShaderCaseDrill(list);
        return registerShaderCase(name, shader, rarity);
    }

    /**
     * Method to register a default implementation of Railgun Shaders
     *
     * @param name              name of the shader
     * @param overlayType       uses IE's existing overlays. To use custom ones, you'll need your own method.
     * @param rarity            Rarity of the shader item
     * @param colour0           grip colour
     * @param colour1           base colour
     * @param colour2           design colour
     * @param additionalTexture additional overlay texture. Null if not needed.
     * @param colourAddtional   colour for the additional texture, if present
     * @return the registered ShaderCase
     */
    public static ShaderCaseRailgun registerShader_Railgun(String name, String overlayType, EnumRarity rarity,
            int colour0, int colour1, int colour2, String additionalTexture, int colourAddtional) {
        ArrayList<ShaderLayer> list = new ArrayList();
        list.add(new ShaderLayer(new ResourceLocation("immersiveengineering:items/shaders/railgun_0"), colour0));
        list.add(new ShaderLayer(new ResourceLocation("immersiveengineering:items/shaders/railgun_0"), colour1));
        list.add(new ShaderLayer(
                new ResourceLocation("immersiveengineering:items/shaders/railgun_1_" + overlayType), colour2));
        if (additionalTexture != null) {
            ResourceLocation rl = additionalTexture.indexOf(58) >= 0 ? new ResourceLocation(additionalTexture)
                    : new ResourceLocation("immersiveengineering:items/shaders/railgun_" + additionalTexture);
            list.add(new ShaderLayer(rl, colourAddtional));
        }
        list.add(new ShaderLayer(new ResourceLocation("immersiveengineering:items/shaders/railgun_uncoloured"),
                0xffffffff));

        ShaderCaseRailgun shader = new ShaderCaseRailgun(list);
        return registerShaderCase(name, shader, rarity);
    }

    /**
     * Method to register a default implementation of Shield Shaders
     *
     * @param name              name of the shader
     * @param overlayType       uses IE's existing overlays. To use custom ones, you'll need your own method.
     * @param rarity            Rarity of the shader item
     * @param colour0           base colour
     * @param colour1           design colour
     * @param additionalTexture additional overlay texture. Null if not needed.
     * @param colourAddtional   colour for the additional texture, if present
     * @return the registered ShaderCase
     */
    public static ShaderCaseShield registerShader_Shield(String name, String overlayType, EnumRarity rarity,
            int colour0, int colour1, String additionalTexture, int colourAddtional) {
        ArrayList<ShaderLayer> list = new ArrayList();
        list.add(new ShaderLayer(new ResourceLocation("immersiveengineering:items/shaders/shield_0"), colour0));
        list.add(new ShaderLayer(new ResourceLocation("immersiveengineering:items/shaders/shield_1_" + overlayType),
                colour1));
        if (additionalTexture != null) {
            ResourceLocation rl = additionalTexture.indexOf(58) >= 0 ? new ResourceLocation(additionalTexture)
                    : new ResourceLocation("immersiveengineering:items/shaders/shield_" + additionalTexture);
            list.add(new ShaderLayer(rl, colourAddtional));
        }
        list.add(new ShaderLayer(new ResourceLocation("immersiveengineering:items/shaders/shield_uncoloured"),
                0xffffffff));

        ShaderCaseShield shader = new ShaderCaseShield(list);
        return registerShaderCase(name, shader, rarity);
    }

    /**
     * Method to register a default implementation of Minecart Shaders
     *
     * @param name              name of the shader
     * @param overlayType       uses IE's existing overlays. To use custom ones, you'll need your own method.
     * @param rarity            Rarity of the shader item
     * @param colour0           base colour
     * @param colour1           design colour
     * @param additionalTexture additional overlay texture. Null if not needed.
     * @param colourAddtional   colour for the additional texture, if present
     * @return the registered ShaderCase
     */
    public static ShaderCaseMinecart registerShader_Minecart(String name, String overlayType, EnumRarity rarity,
            int colour0, int colour1, String additionalTexture, int colourAddtional) {
        ArrayList<ShaderLayer> list = new ArrayList();
        //Minecart textures need .png behind them, since they are used for direct binding, not stitching >_>
        list.add(new ShaderLayer(
                new ResourceLocation("immersiveengineering:textures/models/shaders/minecart_0.png"), colour0));
        list.add(new ShaderLayer(
                new ResourceLocation(
                        "immersiveengineering:textures/models/shaders/minecart_1_" + overlayType + ".png"),
                colour1));
        if (additionalTexture != null) {
            if (additionalTexture.indexOf(58) >= 0)
                list.add(new ShaderLayer(new ResourceLocation(additionalTexture + ".png"), colourAddtional));
            else
                list.add(new ShaderLayer(new ResourceLocation(
                        "immersiveengineering:textures/models/shaders/minecart_" + additionalTexture + ".png"),
                        colourAddtional));
        }
        list.add(new ShaderLayer(
                new ResourceLocation("immersiveengineering:textures/models/shaders/minecart_uncoloured.png"),
                0xffffffff));

        ShaderCaseMinecart shader = new ShaderCaseMinecart(list);
        if (overlayType.equals("1") || overlayType.equals("2") || overlayType.equals("7")) {
            shader.renderSides[1][1] = false;
            shader.renderSides[1][2] = false;
        }
        if (additionalTexture != null) {
            shader.renderSides[2][1] = false;
            shader.renderSides[2][2] = false;
        }
        return registerShaderCase(name, shader, rarity);
    }

    /**
     * Method to register a default implementation of Balloon Shaders
     *
     * @param name              name of the shader
     * @param overlayType       uses IE's existing overlays. To use custom ones, you'll need your own method.
     * @param rarity            Rarity of the shader item
     * @param colour0           base colour
     * @param colour1           design colour
     * @param additionalTexture additional overlay texture. Null if not needed.
     * @param colourAddtional   colour for the additional texture, if present
     * @return the registered ShaderCase
     */
    public static ShaderCaseBalloon registerShader_Balloon(String name, String overlayType, EnumRarity rarity,
            int colour0, int colour1, String additionalTexture, int colourAddtional) {
        ArrayList<ShaderLayer> list = new ArrayList();
        list.add(new ShaderLayer(new ResourceLocation("immersiveengineering:blocks/shaders/balloon_0"), colour0));
        list.add(new ShaderLayer(
                new ResourceLocation("immersiveengineering:blocks/shaders/balloon_1_" + overlayType), colour1));
        if (additionalTexture != null) {
            ResourceLocation rl = additionalTexture.indexOf(58) >= 0 ? new ResourceLocation(additionalTexture)
                    : new ResourceLocation("immersiveengineering:blocks/shaders/balloon_" + additionalTexture);
            list.add(new ShaderLayer(rl, colourAddtional));
        }
        list.add(new ShaderLayer(new ResourceLocation("immersiveengineering:blocks/shaders/balloon_uncoloured"),
                0xffffffff));

        ShaderCaseBalloon shader = new ShaderCaseBalloon(list);
        return registerShaderCase(name, shader, rarity);
    }

    /**
     * Method to register a default implementation of Banner Shaders
     *
     * @param name              name of the shader
     * @param overlayType       uses IE's existing overlays. To use custom ones, you'll need your own method.
     * @param rarity            Rarity of the shader item
     * @param colour0           base colour
     * @param colour1           design colour
     * @param additionalTexture additional overlay texture. Null if not needed.
     * @param colourAddtional   colour for the additional texture, if present
     * @return the registered ShaderCase
     */
    public static ShaderCaseBanner registerShader_Banner(String name, String overlayType, EnumRarity rarity,
            int colour0, int colour1, String additionalTexture, int colourAddtional) {
        ArrayList<ShaderLayer> list = new ArrayList();
        list.add(new ShaderLayer(new ResourceLocation("immersiveengineering:blocks/shaders/banner_0"), colour0));
        list.add(new ShaderLayer(
                new ResourceLocation("immersiveengineering:blocks/shaders/banner_1_" + overlayType), colour1));
        if (additionalTexture != null) {
            ResourceLocation rl = additionalTexture.indexOf(58) >= 0 ? new ResourceLocation(additionalTexture)
                    : new ResourceLocation("immersiveengineering:blocks/shaders/banner_" + additionalTexture);
            list.add(new ShaderLayer(rl, colourAddtional));
        }
        list.add(new ShaderLayer(new ResourceLocation("immersiveengineering:blocks/shaders/banner_uncoloured"),
                0xffffffff));

        ShaderCaseBanner shader = new ShaderCaseBanner(list);
        return registerShaderCase(name, shader, rarity);
    }

    /**
     * A map of shader name to ShaderRegistryEntry, which contains ShaderCases, rarity, weight and loot specifics
     */
    public static Set<IShaderRegistryMethod> shaderRegistrationMethods = new HashSet<IShaderRegistryMethod>();

    public static void addRegistrationMethod(IShaderRegistryMethod method) {
        shaderRegistrationMethods.add(method);
    }

    public interface IShaderRegistryMethod<T extends ShaderCase> {
        /**
         * Method to register shaders for new item types<br>
         * When adding a new shader-accepting item, use this to automatically register all of IE's default shaders for it
         *
         * @param name              name of the shader
         * @param overlayType       overlay name
         * @param colour0           grip colour
         * @param colour1           base colour
         * @param colour2           design colour
         * @param colour3           colour used for bayonet blade
         * @param additionalTexture additional overlay texture. Null if not needed.
         * @param colourAddtional   colour for the additional texture, if present
         * @return the registered ShaderCase
         */
        T apply(String name, String overlayType, EnumRarity rarity, int colour0, int colour1, int colour2,
                int colour3, String additionalTexture, int colourAddtional);
    }

    public static ManualEntry manualEntry;
    public static Item itemShader;
    public static Item itemShaderBag;
    /**
     * List of example items for shader manual entries
     */
    public static List<ItemStack> itemExamples = new ArrayList();

    public static void compileWeight() {
        totalWeight.clear();
        chestLootShaders.clear();
        for (ShaderRegistryEntry entry : shaderRegistry.values()) {
            if (entry.getIsBagLoot()) {
                int entryRarityWeight = rarityWeightMap.get(entry.getRarity());
                for (Map.Entry<EnumRarity, Integer> weightedRarity : rarityWeightMap.entrySet())
                    if (entry.getIsInLowerBags() ? (weightedRarity.getValue() >= entryRarityWeight)
                            : (weightedRarity.getValue() == entryRarityWeight)) {
                        int i = totalWeight.containsKey(weightedRarity.getKey())
                                ? totalWeight.get(weightedRarity.getKey())
                                : 0;
                        totalWeight.put(weightedRarity.getKey(), i + entry.getWeight());
                    }
            }
            if (entry.getIsCrateLoot())
                for (int i = 0; i < entry.getWeight(); i++)
                    chestLootShaders.add(entry.getName());
        }

        sortedRarityMap.clear();
        sortedRarityMap.addAll(ShaderRegistry.rarityWeightMap.keySet());
        Collections.sort(sortedRarityMap, new Comparator<EnumRarity>() {
            @Override
            public int compare(EnumRarity enum0, EnumRarity enum1) {
                return Integer.compare(ShaderRegistry.rarityWeightMap.get(enum0),
                        ShaderRegistry.rarityWeightMap.get(enum1));
            }
        });

        if (manualEntry != null) {
            ArrayList<PositionedItemStack[]> recipes = new ArrayList();
            NonNullList<ItemStack> shaderBags = NonNullList.withSize(ShaderRegistry.sortedRarityMap.size(),
                    ItemStack.EMPTY);
            recipes = new ArrayList();
            for (int i = 0; i < ShaderRegistry.sortedRarityMap.size(); i++) {
                EnumRarity outputRarity = ShaderRegistry.sortedRarityMap.get(i);
                shaderBags.set(i, new ItemStack(itemShaderBag));
                shaderBags.get(i).setTagCompound(new NBTTagCompound());
                shaderBags.get(i).getTagCompound().setString("rarity", outputRarity.toString());
                ArrayList<EnumRarity> upperRarities = ShaderRegistry.getHigherRarities(outputRarity);
                if (!upperRarities.isEmpty()) {
                    ArrayList<ItemStack> inputList = new ArrayList();
                    for (EnumRarity r : upperRarities) {
                        ItemStack bag = new ItemStack(itemShaderBag);
                        bag.setTagCompound(new NBTTagCompound());
                        bag.getTagCompound().setString("rarity", r.toString());
                        inputList.add(bag);
                    }
                    ItemStack s0 = new ItemStack(itemShaderBag, 2);
                    s0.setTagCompound(new NBTTagCompound());
                    s0.getTagCompound().setString("rarity", outputRarity.toString());
                    if (!inputList.isEmpty())
                        recipes.add(new PositionedItemStack[] { new PositionedItemStack(inputList, 33, 0),
                                new PositionedItemStack(s0, 69, 0) });
                    //               inputList = new ArrayList();
                    //               for(ShaderRegistryEntry entry : ShaderRegistry.shaderRegistry.values())
                    //                  if(upperRarities.contains(entry.getRarity()))
                    //                  {
                    //                     ItemStack shader = new ItemStack(itemShader);
                    //                     shader.setTagCompound(new NBTTagCompound());
                    //                     shader.getTagCompound().setString("shader_name",  entry.getName());
                    //                     inputList.add(shader);
                    //                  }
                    //               ItemStack s1 = new ItemStack(itemShaderBag);
                    //               s1.setTagCompound(new NBTTagCompound());
                    //               s1.getTagCompound().setString("rarity", outputRarity.toString());
                    //               if(!inputList.isEmpty())
                    //                  recipes.add(new PositionedItemStack[]{ new PositionedItemStack(inputList, 33, 0), new PositionedItemStack(s1, 69, 0)});
                }
            }
            manualEntry.getPages()[2] = new ManualPages.ItemDisplay(ManualHelper.getManual(), "shader2",
                    shaderBags);
            manualEntry.getPages()[3] = new ManualPages.CraftingMulti(ManualHelper.getManual(), "shader3",
                    (Object[]) recipes.toArray(new PositionedItemStack[recipes.size()][3]));
        }
    }

    public static void recalculatePlayerTotalWeight(String player) {
        if (!playerTotalWeight.containsKey(player))
            playerTotalWeight.put(player, new HashMap<EnumRarity, Integer>());
        else
            playerTotalWeight.get(player).clear();
        List<String> received = receivedShaders.get(player);
        for (ShaderRegistryEntry entry : shaderRegistry.values())
            if (entry.getIsBagLoot()) {
                int entryRarityWeight = rarityWeightMap.get(entry.getRarity());
                for (Map.Entry<EnumRarity, Integer> weightedRarity : rarityWeightMap.entrySet())
                    if (entry.getIsInLowerBags() ? (weightedRarity.getValue() >= entryRarityWeight)
                            : (weightedRarity.getValue() == entryRarityWeight)) {
                        int weight = playerTotalWeight.get(player).containsKey(weightedRarity.getKey())
                                ? playerTotalWeight.get(player).get(weightedRarity.getKey())
                                : 0;
                        int value = entry.getWeight();
                        if (received.contains(entry.getName()))
                            value = 1;
                        playerTotalWeight.get(player).put(weightedRarity.getKey(), weight + value);
                    }
            }
    }

    public static String getRandomShader(String player, Random rand, EnumRarity minRarity, boolean addToReceived) {
        int total = 0;
        if (!playerTotalWeight.containsKey(player))
            playerTotalWeight.put(player, totalWeight);
        total = playerTotalWeight.get(player).get(minRarity);

        String shader = null;
        int minWeight = rarityWeightMap.get(minRarity);
        int weight = total < 1 ? total : rand.nextInt(total);
        for (ShaderRegistryEntry entry : shaderRegistry.values())
            if (entry.getIsBagLoot()) {
                int entryRarityWeight = rarityWeightMap.get(entry.getRarity());
                if (entry.getIsInLowerBags() ? (minWeight >= entryRarityWeight)
                        : (minWeight == entryRarityWeight)) {
                    int value = entry.getWeight();
                    if (receivedShaders.get(player).contains(entry.getName()))
                        value = 1;
                    weight -= value;
                    if (weight <= 0) {
                        shader = entry.getName();
                        break;
                    }
                }
            }
        if (addToReceived) {
            if (!receivedShaders.get(player).contains(shader))
                receivedShaders.put(player, shader);
            recalculatePlayerTotalWeight(player);
        }
        return shader;
    }

    public static EnumRarity getLowerRarity(EnumRarity rarity) {
        int idx = sortedRarityMap.indexOf(rarity);
        int weight = rarityWeightMap.get(rarity);
        for (int next = idx + 1; next < sortedRarityMap.size(); next++)
            if (rarityWeightMap.get(sortedRarityMap.get(next)) > weight)
                return sortedRarityMap.get(next);
        return null;
    }

    public static ArrayList<EnumRarity> getAllLowerRarities(EnumRarity rarity) {
        ArrayList<EnumRarity> list = new ArrayList<EnumRarity>();
        int idx = sortedRarityMap.indexOf(rarity);
        int weight = rarityWeightMap.get(rarity);
        for (int next = idx + 1; next < sortedRarityMap.size(); next++)
            if (rarityWeightMap.get(sortedRarityMap.get(next)) > weight)
                list.add(sortedRarityMap.get(next));
        return list;
    }

    public static ArrayList<EnumRarity> getHigherRarities(EnumRarity rarity) {
        ArrayList<EnumRarity> list = new ArrayList<EnumRarity>();
        int idx = sortedRarityMap.indexOf(rarity);
        if (idx <= 0)
            return list;
        int next = idx - 1;
        int weight = rarityWeightMap.get(rarity);
        int lowerWeight = -1;
        for (; next >= 0; next--) {
            EnumRarity r = sortedRarityMap.get(next);
            int rWeight = rarityWeightMap.get(r);
            if (rWeight < weight && (lowerWeight == -1 || rWeight >= lowerWeight)) {
                list.add(r);
                lowerWeight = rWeight;
            }
        }
        return list;
    }

    public static ArrayList<EnumRarity> getAllHigherRarities(EnumRarity rarity) {
        ArrayList<EnumRarity> list = new ArrayList<EnumRarity>();
        int idx = sortedRarityMap.indexOf(rarity);
        if (idx <= 0)
            return list;
        int next = idx - 1;
        int weight = rarityWeightMap.get(rarity);
        for (; next >= 0; next--) {
            EnumRarity r = sortedRarityMap.get(next);
            int rWeight = rarityWeightMap.get(r);
            if (rWeight < weight)
                list.add(r);
        }
        return list;
    }

    public static Triple<ItemStack, ShaderRegistryEntry, ShaderCase> getStoredShaderAndCase(ItemStack itemStack) {
        if (!itemStack.isEmpty() && itemStack.hasCapability(CapabilityShader.SHADER_CAPABILITY, null)) {
            CapabilityShader.ShaderWrapper wrapper = itemStack.getCapability(CapabilityShader.SHADER_CAPABILITY,
                    null);
            if (wrapper != null)
                return getStoredShaderAndCase(wrapper);
        }
        return null;
    }

    public static Triple<ItemStack, ShaderRegistryEntry, ShaderCase> getStoredShaderAndCase(
            CapabilityShader.ShaderWrapper wrapper) {
        ItemStack shader = wrapper.getShaderItem();
        if (!shader.isEmpty() && shader.getItem() instanceof IShaderItem) {
            IShaderItem iShaderItem = ((IShaderItem) shader.getItem());
            ShaderRegistryEntry registryEntry = shaderRegistry.get(iShaderItem.getShaderName(shader));
            if (registryEntry != null)
                return Triple.of(shader, registryEntry, registryEntry.getCase(wrapper.getShaderType()));
        }
        return null;
    }

    public static class ShaderRegistryEntry {
        public String name;
        public HashMap<String, ShaderCase> cases = new HashMap<String, ShaderCase>();
        public EnumRarity rarity;
        public int weight;
        public boolean isCrateLoot;
        public boolean isBagLoot;
        public boolean isInLowerBags = true;

        public String info_set;
        public String info_reference;
        public String info_details;
        public IngredientStack replicationCost;

        public IShaderEffectFunction effectFunction;
        private static final IShaderEffectFunction DEFAULT_EFFECT = (world, shader, item, shaderType, pos, dir,
                scale) -> {
        };

        public ShaderRegistryEntry(String name, EnumRarity rarity, List<ShaderCase> cases) {
            this.name = name;
            this.rarity = rarity;
            this.weight = rarityWeightMap.get(rarity);
            for (ShaderCase sCase : cases)
                this.cases.put(sCase.getShaderType(), sCase);
        }

        public ShaderRegistryEntry(String name, EnumRarity rarity, ShaderCase... cases) {
            this(name, rarity, Arrays.asList(cases));
        }

        public ShaderRegistryEntry addCase(String type, ShaderCase sCase) {
            this.cases.put(type, sCase);
            return this;
        }

        public ShaderRegistryEntry addCase(ShaderCase sCase) {
            return this.addCase(sCase.getShaderType(), sCase);
        }

        public ShaderCase getCase(String type) {
            return this.cases.get(type);
        }

        public List<ShaderCase> getCases() {
            return new ArrayList(this.cases.values());
        }

        public String getName() {
            return this.name;
        }

        public EnumRarity getRarity() {
            return this.rarity;
        }

        public ShaderRegistryEntry setWeight(int weight) {
            this.weight = weight;
            return this;
        }

        public int getWeight() {
            return this.weight;
        }

        public ShaderRegistryEntry setCrateLoot(boolean b) {
            this.isCrateLoot = b;
            return this;
        }

        public boolean getIsCrateLoot() {
            return this.isCrateLoot;
        }

        public ShaderRegistryEntry setBagLoot(boolean b) {
            this.isBagLoot = b;
            return this;
        }

        public boolean getIsBagLoot() {
            return this.isBagLoot;
        }

        public ShaderRegistryEntry setInLowerBags(boolean b) {
            this.isInLowerBags = b;
            return this;
        }

        public boolean getIsInLowerBags() {
            return this.isInLowerBags;
        }

        public ShaderRegistryEntry setInfo(@Nullable String set, @Nullable String reference,
                @Nullable String details) {
            this.info_set = set;
            this.info_reference = reference;
            this.info_details = details;
            return this;
        }

        public ShaderRegistryEntry setReplicationCost(@Nonnull IngredientStack replicationCost) {
            this.replicationCost = replicationCost;
            return this;
        }

        public ShaderRegistryEntry setEffectFunction(@Nonnull IShaderEffectFunction effectFunction) {
            this.effectFunction = effectFunction;
            return this;
        }

        @Nonnull
        public IShaderEffectFunction getEffectFunction() {
            if (effectFunction != null)
                return effectFunction;
            return DEFAULT_EFFECT;
        }
    }
}