aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/gregtech/api/util/GT_RecipeMapUtil.java
blob: ee524c913ce2d579888006449d3bac8e42f3fce1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
package gregtech.api.util;

import static gregtech.api.enums.Mods.GregTech;
import static gregtech.api.util.GT_Config.getStackConfigName;
import static gregtech.api.util.GT_Utility.isArrayEmptyOrNull;

import java.util.*;
import java.util.function.Consumer;
import java.util.function.Function;

import net.minecraft.item.ItemStack;
import net.minecraftforge.fluids.FluidStack;

import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Multimap;

import cpw.mods.fml.common.Loader;
import gnu.trove.list.TIntList;
import gnu.trove.list.array.TIntArrayList;
import gregtech.api.interfaces.IGT_RecipeMap;

/**
 * Define helpers useful in the creation of recipe maps.
 */
// Do not place arbitrary stuff here! These are all statically imported in GT_Recipe.java file.
public class GT_RecipeMapUtil {

    public static final Function<GT_Recipe, GT_Recipe> ALL_FAKE_RECIPE = r -> {
        r.mFakeRecipe = true;
        return r;
    };

    public static final Function<GT_Recipe, String> FIRST_FLUID_INPUT = r -> isArrayEmptyOrNull(r.mFluidInputs) ? null
            : r.mFluidInputs[0].getFluid()
                               .getName();
    public static final Function<GT_Recipe, String> FIRST_FLUID_OUTPUT = r -> isArrayEmptyOrNull(r.mFluidInputs) ? null
            : r.mFluidOutputs[0].getFluid()
                                .getName();
    public static final Function<GT_Recipe, String> FIRST_FLUIDSTACK_INPUT = r -> isArrayEmptyOrNull(r.mFluidInputs)
            ? null
            : r.mFluidInputs[0].getUnlocalizedName();
    public static final Function<GT_Recipe, String> FIRST_FLUIDSTACK_OUTPUT = r -> isArrayEmptyOrNull(r.mFluidOutputs)
            ? null
            : r.mFluidOutputs[0].getUnlocalizedName();
    public static final Function<GT_Recipe, String> FIRST_ITEM_INPUT = r -> isArrayEmptyOrNull(r.mInputs) ? null
            : getStackConfigName(r.mInputs[0]);
    public static final Function<GT_Recipe, String> FIRST_ITEM_OUTPUT = r -> isArrayEmptyOrNull(r.mOutputs) ? null
            : getStackConfigName(r.mOutputs[0]);
    public static final Function<GT_Recipe, String> FIRST_ITEM_OR_FLUID_INPUT = r -> isArrayEmptyOrNull(r.mInputs)
            ? isArrayEmptyOrNull(r.mFluidInputs) ? null
                    : r.mFluidInputs[0].getFluid()
                                       .getName()
            : getStackConfigName(r.mInputs[0]);
    public static final Function<GT_Recipe, String> FIRST_ITEM_OR_FLUID_OUTPUT = r -> isArrayEmptyOrNull(r.mOutputs)
            ? isArrayEmptyOrNull(r.mFluidOutputs) ? null
                    : r.mFluidOutputs[0].getFluid()
                                        .getName()
            : getStackConfigName(r.mOutputs[0]);
    private static final Map<String, IGT_RecipeMap> addonRecipeMaps = new HashMap<>();
    private static final Multimap<String, Consumer<IGT_RecipeMap>> delayedActions = ArrayListMultimap.create();

    public static final Set<GT_RecipeBuilder.MetadataIdentifier<Integer>> SPECIAL_VALUE_ALIASES = new HashSet<>();

    public static <T> T[] appendArray(T[] arr, T val) {
        T[] newArr = Arrays.copyOf(arr, arr.length + 1);
        newArr[arr.length] = val;
        return newArr;
    }

    public static GT_RecipeTemplate asTemplate(GT_Recipe r) {
        return asTemplate(r, false);
    }

    public static GT_RecipeTemplate asTemplate(GT_Recipe r, boolean includeTemplate) {
        return new GT_RecipeTemplate(r, includeTemplate);
    }

    public static List<GT_Recipe> buildRecipeForMultiblock(GT_RecipeBuilder b) {
        return buildOrEmpty(convertCellToFluid(b, true));

    }

    public static List<GT_Recipe> buildRecipeForMultiblockNoCircuit(GT_RecipeBuilder b) {
        return buildOrEmpty(convertCellToFluid(b, false));
    }

    public static GT_RecipeBuilder convertCellToFluid(GT_RecipeBuilder b, boolean removeIntegratedCircuit) {
        List<ItemStack> itemInputs = new ArrayList<>(Arrays.asList(b.getItemInputsBasic()));
        List<ItemStack> itemOutputs = new ArrayList<>(Arrays.asList(b.getItemOutputs()));
        List<FluidStack> fluidInputs = new ArrayList<>(Arrays.asList(b.getFluidInputs()));
        List<FluidStack> fluidOutputs = new ArrayList<>(Arrays.asList(b.getFluidOutputs()));
        TIntList chances = b.getChances() != null ? new TIntArrayList(b.getChances()) : null;
        cellToFluid(itemInputs, fluidInputs, removeIntegratedCircuit, null);
        cellToFluid(itemOutputs, fluidOutputs, removeIntegratedCircuit, chances);
        return b.itemInputs(itemInputs.toArray(new ItemStack[0]))
                .itemOutputs(itemOutputs.toArray(new ItemStack[0]), chances != null ? chances.toArray() : null)
                .fluidInputs(fluidInputs.toArray(new FluidStack[0]))
                .fluidOutputs(fluidOutputs.toArray(new FluidStack[0]));
    }

    private static void cellToFluid(List<ItemStack> items, List<FluidStack> fluids, boolean removeIntegratedCircuit,
            TIntList chances) {
        for (int i = items.size() - 1; i >= 0; i--) {
            ItemStack item = items.get(i);
            if (GT_Utility.getFluidForFilledItem(item, true) != null || GT_Utility.isCellEmpty(item)
                    || (removeIntegratedCircuit && GT_Utility.isAnyIntegratedCircuit(item))) {
                fluids.add(GT_Utility.convertCellToFluid(item));
                items.remove(i);
                if (chances != null) chances.removeAt(i);
            }
        }
    }

    public static List<GT_Recipe> buildOrEmpty(GT_RecipeBuilder builder) {
        return builder.build()
                      .map(Collections::singletonList)
                      .orElse(Collections.emptyList());
    }

    /**
     * Register a recipe map as part of your mod's public API under your modid and your given identifier.
     *
     * @param identifier
     * @param recipeMap
     * @param dependencies fully qualified identifier of dependent recipe maps. scheduler will only add recipes to one
     *                     of the dependent recipe maps and this recipe map concurrently, guaranteeing thread safety.
     *                     Currently unused, but you are advised to fill them, so that when The Day (tm) comes we don't
     *                     end up with a bunch of weird concurrency bugs.
     */
    public static void registerRecipeMap(String identifier, IGT_RecipeMap recipeMap,
            RecipeMapDependency... dependencies) {
        String modId = Loader.instance()
                             .activeModContainer()
                             .getModId();
        if (GregTech.ID.equals(modId)) throw new IllegalStateException(
                "do not register recipe map under the name of gregtech! do it in your own preinit!");
        String id = modId + "@" + identifier;
        addonRecipeMaps.put(id, recipeMap);
        for (Consumer<IGT_RecipeMap> action : delayedActions.get(id)) {
            action.accept(recipeMap);
        }
    }

    /**
     * Use this to register recipes for a recipe map in addon not present at compile time.
     * <p>
     * Do not use this for recipes maps already in {@link GT_RecipeConstants}. None of them will be available via this
     * interface!
     *
     * @param identifier     recipe map id
     * @param registerAction DO NOT ADD RECIPES TO MAPS OTHER THAN THE ONE PASSED TO YOU. DO NOT DO ANYTHING OTHER THAN
     *                       ADDING RECIPES TO THIS R
     */
    public static void registerRecipesFor(String modid, String identifier, Consumer<IGT_RecipeMap> registerAction) {
        String id = modid + "@" + identifier;
        IGT_RecipeMap map = addonRecipeMaps.get(id);
        if (map == null) delayedActions.put(id, registerAction);
        else registerAction.accept(map);
    }

    public static final class GT_RecipeTemplate {

        private final GT_Recipe template;
        private final List<GT_Recipe> derivatives = new ArrayList<>();

        private GT_RecipeTemplate(GT_Recipe template, boolean includeTemplate) {
            this.template = template;
            if (includeTemplate) derivatives.add(template);
        }

        public GT_Recipe derive() {
            GT_Recipe derived = template.copyShallow();
            derivatives.add(derived);
            return derived;
        }

        public List<GT_Recipe> getAll() {
            // fix shallow references
            Set<Object> references = Collections.newSetFromMap(new IdentityHashMap<>());
            for (GT_Recipe r : derivatives) {
                if (!references.add(r.mInputs)) r.mInputs = r.mInputs.clone();
                if (!references.add(r.mOutputs)) r.mOutputs = r.mOutputs.clone();
                if (!references.add(r.mFluidInputs)) r.mFluidInputs = r.mFluidInputs.clone();
                if (!references.add(r.mFluidOutputs)) r.mFluidOutputs = r.mFluidOutputs.clone();
            }
            return derivatives;
        }
    }

    public static final class RecipeMapDependency {

        private final IGT_RecipeMap obj;
        private final String id;

        public RecipeMapDependency(IGT_RecipeMap obj, String id) {
            this.obj = obj;
            this.id = id;
        }

        public static RecipeMapDependency create(String id) {
            return new RecipeMapDependency(null, id);
        }

        public static RecipeMapDependency create(IGT_RecipeMap obj) {
            return new RecipeMapDependency(obj, null);
        }
    }
}