Add dynamic bee tree requirement slots (#12)

This commit is contained in:
ABKQPO authored and GitHub committed 2026-10-12 19:10:07 +02:00
1 parent a9a5f1e1b2
commit 1a6e8be76d
13 files changed
+382 -78

No files matched your search

+2 -2
View File
@@ -34,6 +34,6 @@
* For more details, see https://docs.gradle.org/8.0.1/userguide/java_library_plugin.html#sec:java_library_configurations_graph
*/
dependencies {
api("com.github.GTNewHorizons:NotEnoughItems:2.8.114-GTNH:dev")
api("com.github.GTNewHorizons:ForestryMC:4.11.31:dev")
api("com.github.GTNewHorizons:NotEnoughItems:2.8.155-GTNH:dev")
api("com.github.GTNewHorizons:ForestryMC:4.11.38:dev")
}
@@ -4,6 +4,8 @@ import codechicken.nei.api.API;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import hellfirepvp.beebetteratbees.client.gui.BBABGuiRecipeTreeHandler;
import hellfirepvp.beebetteratbees.client.requirements.MutationConditionRequirementProvider;
import hellfirepvp.beebetteratbees.client.requirements.RequirementResolvers;
import hellfirepvp.beebetteratbees.common.CommonProxy;
/**
@@ -21,6 +23,7 @@ public class ClientProxy extends CommonProxy {
@SideOnly(Side.CLIENT)
private void registerNEIStuff() {
RequirementResolvers.register(new MutationConditionRequirementProvider());
BBABGuiRecipeTreeHandler handler = new BBABGuiRecipeTreeHandler();
API.registerRecipeHandler(handler);
API.registerUsageHandler(handler);
@@ -20,6 +20,7 @@ import forestry.api.genetics.IIndividual;
import forestry.api.genetics.ISpeciesRoot;
import hellfirepvp.beebetteratbees.client.gui.CachedBeeMutationTree.ChanceInfoNode;
import hellfirepvp.beebetteratbees.client.gui.CachedBeeMutationTree.IMutationNode;
import hellfirepvp.beebetteratbees.client.requirements.RequirementResolvers;
import hellfirepvp.beebetteratbees.common.BeeBetterAtBees;
import hellfirepvp.beebetteratbees.common.ModConfig;
@@ -65,11 +66,22 @@ public class BBABGuiRecipeTreeHandler extends AbstractTreeGUIHandler {
public void loadCraftingRecipes(String outputId, Object... results) {
if (speciesRoot == null) return;
if (outputId.equals("item")) {
if (outputId.equals("all")) {
loadAllRecipes();
} else if (outputId.equals("item")) {
loadCraftingRecipes((ItemStack) results[0]);
}
}
private void loadAllRecipes() {
for (IBeeMutation mutation : speciesRoot.getMutations(false)) {
if (!mutation.isSecret() || ModConfig.shouldShowSecretRecipes) {
this.arecipes.add(new CachedBeeMutationTree(mutation));
}
}
cleanupDuplicateRecipes();
}
public void loadCraftingRecipes(ItemStack result) {
if (speciesRoot == null) return;
@@ -118,23 +130,30 @@ public class BBABGuiRecipeTreeHandler extends AbstractTreeGUIHandler {
Point pos = GuiDraw.getMousePosition();
Point guiOffset = new Point(gui.guiLeft, gui.guiTop);
Point recipeOffset = gui.getRecipePosition(recipe);
int relativeX = pos.x - guiOffset.x - recipeOffset.x;
int relativeY = pos.y - guiOffset.y - recipeOffset.y;
for (IMutationNode mutation : cachedTree.getMutationNodesToRender()) {
if (mutation instanceof ChanceInfoNode chanceInfoNode && chanceInfoNode
.containsPoint(pos.x - guiOffset.x - recipeOffset.x, pos.y - guiOffset.y - recipeOffset.y)) {
if (mutation instanceof ChanceInfoNode chanceInfoNode
&& chanceInfoNode.containsPoint(relativeX, relativeY)) {
return new LinkedList<>(chanceInfoNode.infoLines);
}
}
}
return super.handleTooltip(gui, currenttip, recipe);
}
@Override
public int getRecipeHeight(int recipe) {
return ((CachedBeeMutationTree) this.arecipes.get(recipe)).getRecipeHeight();
}
@Override
public void loadUsageRecipes(String inputId, Object... ingredients) {
if (speciesRoot == null) return;
if (inputId.equals("item")) {
if (inputId.equals("all")) {
loadAllRecipes();
} else if (inputId.equals("item")) {
loadUsageRecipes((ItemStack) ingredients[0]);
}
}
@@ -142,33 +161,21 @@ public class BBABGuiRecipeTreeHandler extends AbstractTreeGUIHandler {
public void loadUsageRecipes(ItemStack ingredient) {
if (speciesRoot == null) return;
if (!speciesRoot.isMember(ingredient)) {
return;
IAlleleSpecies species = null;
if (speciesRoot.isMember(ingredient)) {
IIndividual individual = speciesRoot.getMember(ingredient);
var genome = individual == null ? null : individual.getGenome();
if (genome != null) species = genome.getPrimary();
}
IIndividual individual = speciesRoot.getMember(ingredient);
if (individual == null) {
BeeBetterAtBees.log.warn("IIndividual is null searching recipe for %s", ingredient.toString());
return;
}
if (individual.getGenome() == null) {
BeeBetterAtBees.log.warn("Genome is null when searching recipe for %s", ingredient.toString());
return;
}
if (individual.getGenome()
.getPrimary() == null) {
BeeBetterAtBees.log.warn("Species is null when searching recipe for %s", ingredient.toString());
return;
}
IAlleleSpecies species = individual.getGenome()
.getPrimary();
for (IBeeMutation mutation : speciesRoot.getMutations(false)) {
if (mutation.getAllele0()
if (mutation.isSecret() && !ModConfig.shouldShowSecretRecipes) continue;
boolean matchesSpecies = species != null && (mutation.getAllele0()
.equals(species)
|| mutation.getAllele1()
.equals(species)) {
if (!mutation.isSecret() || ModConfig.shouldShowSecretRecipes) {
this.arecipes.add(new CachedBeeMutationTree(mutation));
}
.equals(species));
if (matchesSpecies || RequirementResolvers.matches(mutation, ingredient)) {
this.arecipes.add(new CachedBeeMutationTree(mutation));
}
}
cleanupDuplicateRecipes();
@@ -6,6 +6,7 @@ import java.awt.Color;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
@@ -20,6 +21,10 @@ import codechicken.nei.PositionedStack;
import forestry.api.apiculture.IBeeMutation;
import forestry.api.genetics.IAllele;
import forestry.api.genetics.IAlleleSpecies;
import hellfirepvp.beebetteratbees.client.requirements.BlockRequirement;
import hellfirepvp.beebetteratbees.client.requirements.RequirementLayout;
import hellfirepvp.beebetteratbees.client.requirements.RequirementResolvers;
import hellfirepvp.beebetteratbees.client.requirements.RequirementSlot;
import hellfirepvp.beebetteratbees.client.util.ColorUtils;
import hellfirepvp.beebetteratbees.client.util.SimpleBinaryTree;
import hellfirepvp.beebetteratbees.common.ModConfig;
@@ -32,14 +37,14 @@ import hellfirepvp.beebetteratbees.common.ModConfig;
*/
public class CachedBeeMutationTree extends CachedRecipe {
protected static interface IMutationNode {
public interface IMutationNode {
boolean containsPoint(int x, int y);
void renderNode();
}
protected static class LineNode implements IMutationNode {
public static class LineNode implements IMutationNode {
public final double lx;
public final double ly;
@@ -83,7 +88,7 @@ public class CachedBeeMutationTree extends CachedRecipe {
}
}
protected static class ChanceInfoNode implements IMutationNode {
public static class ChanceInfoNode implements IMutationNode {
public final int x;
public final int y;
@@ -125,11 +130,7 @@ public class CachedBeeMutationTree extends CachedRecipe {
}
}
// RENDERING BB
// X = 15 to 135 (Size: 120)
// Y = 10 to 100 (Size: 90)
private static final int MIN_X = 15, MAX_X = 135;
private static final int MIN_X = 2, MAX_X = 162;
private static final int X_SEPERATION_THRESHOLD = 7;
private static final int Y_OFFSET = 0;
private static final Color LINE_BLACK = new Color(ColorUtils.neiLineBlack.getColor(), true);
@@ -139,6 +140,7 @@ public class CachedBeeMutationTree extends CachedRecipe {
private static final int OFFSET_CORRECTION = 8;
private static final int POSSIBLE_CHILD_OFFSET = 16;
private static final int LEVEL_STEP = 50;
private final SimpleBinaryTree<IAllele> mutationTree;
private final List<PositionedMutationNodeStack> evaluatedBeePositions;
@@ -146,6 +148,7 @@ public class CachedBeeMutationTree extends CachedRecipe {
public final boolean oversized;
private PositionedMutationNodeStack rootStack;
private List<IMutationNode> mutationNodesToRender = new LinkedList<>();
private final List<RequirementSlot> requirementSlots = new ArrayList<>();
public CachedBeeMutationTree(IBeeMutation parentMutation) {
// parentMutation.getTemplate() Gets results primary at array[0], secondary at array[1]
@@ -184,7 +187,7 @@ public class CachedBeeMutationTree extends CachedRecipe {
oversized = false;
return; // In case the root is a leaf, there is nothing to display anyway except the root.
}
int yStep = 110 / maxTotalDepth;
int yStep = LEVEL_STEP;
this.oversized = checkSeparationWidth(maxTotalDepth, X_SEPERATION_THRESHOLD);
this.evaluatedMaxX = MAX_X;
@@ -207,25 +210,17 @@ public class CachedBeeMutationTree extends CachedRecipe {
this.evaluatedMaxX,
iterationDepth - 1);
List<IBeeMutation> mutationsToRoot = getMutationsWithResult(
mutationTree.getRoot()
.getValue());
float ch = -1;
Collection<String> requirements = new LinkedList<>();
if (!mutationsToRoot.isEmpty()) {
IBeeMutation mut = mutationsToRoot.get(0);
ch = mut.getBaseChance();
try {
requirements = mut.getSpecialConditions();
} catch (Throwable ignored) {}
}
float ch = parentMutation.getBaseChance();
Collection<String> requirements = getSpecialConditions(parentMutation);
this.rootStack = new PositionedMutationNodeStack(
createStack(
(IAlleleSpecies) mutationTree.getRoot()
.getValue(),
BEE_TYPE_PRINCESS),
(MIN_X + this.evaluatedMaxX) / 2,
(IAlleleSpecies) mutationTree.getRoot()
.getValue(),
(MIN_X + this.evaluatedMaxX) / 2 - OFFSET_CORRECTION,
Y_OFFSET,
ch,
requirements,
@@ -234,6 +229,38 @@ public class CachedBeeMutationTree extends CachedRecipe {
true);
generateMutationNodes(rootStack);
buildRequirementSlots(parentMutation);
}
private void buildRequirementSlots(IBeeMutation parentMutation) {
addRequirements(rootStack, RequirementResolvers.resolve(parentMutation));
List<PositionedMutationNodeStack> orderedStacks = new ArrayList<>(evaluatedBeePositions);
orderedStacks.sort(
Comparator.comparingInt((PositionedMutationNodeStack stack) -> stack.rely)
.thenComparingInt(stack -> stack.relx));
for (PositionedMutationNodeStack stack : orderedStacks) {
addRequirements(stack, resolveRequirements(stack.species));
}
}
private void addRequirements(PositionedMutationNodeStack stack, List<BlockRequirement> requirements) {
if (stack == null || requirements.isEmpty()) return;
List<RequirementSlot> placed = RequirementLayout.place(requirements, stack.relx, stack.rely);
requirementSlots.addAll(placed);
}
private List<BlockRequirement> resolveRequirements(IAllele species) {
List<IBeeMutation> mutations = getMutationsWithResult(species);
return mutations.isEmpty() ? Collections.emptyList() : RequirementResolvers.resolve(mutations.get(0));
}
private static Collection<String> getSpecialConditions(IBeeMutation mutation) {
try {
Collection<String> conditions = mutation.getSpecialConditions();
return conditions == null ? Collections.<String>emptyList() : conditions;
} catch (Throwable ignored) {
return Collections.emptyList();
}
}
private void generateMutationNodes(PositionedMutationNodeStack nodeStack) {
@@ -322,8 +349,8 @@ public class CachedBeeMutationTree extends CachedRecipe {
}
private boolean checkSeparationWidth(int maxTotalDepth, int xSeparationThreshold) {
double maxDivision = Math.pow(2, maxTotalDepth + 1);
int resultingLLWidth = (int) (120 / maxDivision); // LowestLevelWidth
int leafCount = 1 << maxTotalDepth;
int resultingLLWidth = (MAX_X - MIN_X) / leafCount;
return resultingLLWidth < xSeparationThreshold;
}
@@ -334,19 +361,16 @@ public class CachedBeeMutationTree extends CachedRecipe {
if (iterationMaxCount < 0 || node.getMaxFollowingDepth() <= 0) {
List<IBeeMutation> mutationsToRoot = getMutationsWithResult(node.getValue());
float ch = -1;
Collection<String> requirements = new LinkedList<>();
Collection<String> requirements = Collections.emptyList();
if (!mutationsToRoot.isEmpty()) {
IBeeMutation mut = mutationsToRoot.get(0);
ch = mut.getBaseChance();
try {
requirements = mut.getSpecialConditions();
} catch (Throwable tr) {
requirements = new LinkedList<>();
}
ch = mutationsToRoot.get(0)
.getBaseChance();
requirements = getSpecialConditions(mutationsToRoot.get(0));
}
PositionedMutationNodeStack leaf = new PositionedMutationNodeStack( // Leaf
createStack((IAlleleSpecies) node.getValue(), BEE_TYPE_DRONE),
center,
(IAlleleSpecies) node.getValue(),
center - OFFSET_CORRECTION,
minY,
ch,
requirements,
@@ -359,20 +383,17 @@ public class CachedBeeMutationTree extends CachedRecipe {
List<IBeeMutation> mutations = getMutationsWithResult(node.getValue());
float ch = -1;
Collection<String> requirements = new LinkedList<>();
Collection<String> requirements = Collections.emptyList();
if (!mutations.isEmpty()) {
IBeeMutation mut = mutations.get(0);
ch = mut.getBaseChance();
try {
requirements = mut.getSpecialConditions();
} catch (Throwable tr) {
requirements = new LinkedList<>();
}
ch = mutations.get(0)
.getBaseChance();
requirements = getSpecialConditions(mutations.get(0));
}
PositionedMutationNodeStack outNode = new PositionedMutationNodeStack(
createStack((IAlleleSpecies) node.getValue(), BEE_TYPE_DRONE),
center,
(IAlleleSpecies) node.getValue(),
center - OFFSET_CORRECTION,
minY,
ch,
requirements,
@@ -413,9 +434,24 @@ public class CachedBeeMutationTree extends CachedRecipe {
return mutationNodesToRender;
}
public List<RequirementSlot> getRequirementSlots() {
return requirementSlots;
}
public int getRecipeHeight() {
int maxY = RequirementLayout.MIN_RECIPE_HEIGHT;
for (RequirementSlot slot : requirementSlots) {
maxY = Math.max(maxY, slot.rely + RequirementLayout.SLOT_SIZE + 4);
}
for (PositionedMutationNodeStack stack : evaluatedBeePositions) maxY = Math.max(maxY, stack.rely + 24);
return maxY;
}
@Override
public List<PositionedStack> getIngredients() {
return new ArrayList<>(evaluatedBeePositions);
List<PositionedStack> result = new ArrayList<>(evaluatedBeePositions);
result.addAll(requirementSlots);
return result;
}
public static class PositionedMutationNodeStack extends PositionedStack {
@@ -423,10 +459,11 @@ public class CachedBeeMutationTree extends CachedRecipe {
public final boolean hasPossibleChildren;
public final float baseChance;
public final Collection<String> requirements;
public final IAlleleSpecies species;
public final PositionedMutationNodeStack leftChild, rightChild;
public PositionedMutationNodeStack(Object object, int x, int y, boolean genPerms, float baseChance,
Collection<String> requirementInfo, PositionedMutationNodeStack leftChild,
public PositionedMutationNodeStack(Object object, IAlleleSpecies species, int x, int y, boolean genPerms,
float baseChance, Collection<String> requirementInfo, PositionedMutationNodeStack leftChild,
PositionedMutationNodeStack rightChild, boolean hasPossibleChildren) {
super(object, x, y, genPerms);
this.leftChild = leftChild;
@@ -434,9 +471,10 @@ public class CachedBeeMutationTree extends CachedRecipe {
this.hasPossibleChildren = hasPossibleChildren;
this.baseChance = baseChance;
this.requirements = requirementInfo;
this.species = species;
}
public PositionedMutationNodeStack(Object object, int x, int y, float baseChance,
public PositionedMutationNodeStack(Object object, IAlleleSpecies species, int x, int y, float baseChance,
Collection<String> requirementInfo, PositionedMutationNodeStack leftChild,
PositionedMutationNodeStack rightChild, boolean hasPossibleChildren) {
super(object, x, y);
@@ -445,6 +483,7 @@ public class CachedBeeMutationTree extends CachedRecipe {
this.hasPossibleChildren = hasPossibleChildren;
this.baseChance = baseChance;
this.requirements = requirementInfo;
this.species = species;
}
}
@@ -0,0 +1,29 @@
package hellfirepvp.beebetteratbees.client.requirements;
import java.util.Collections;
import java.util.List;
import net.minecraft.item.ItemStack;
public class BlockRequirement {
private final List<ItemStack> candidates;
private final String label;
public BlockRequirement(List<ItemStack> candidates, String label) {
this.candidates = Collections.unmodifiableList(RequirementStacks.sanitize(candidates));
this.label = label;
}
public List<ItemStack> getCandidates() {
return candidates;
}
public String getLabel() {
return label;
}
public boolean isEmpty() {
return candidates.isEmpty();
}
}
@@ -0,0 +1,8 @@
package hellfirepvp.beebetteratbees.client.requirements;
import java.util.List;
public interface IBlockRequirementProvider {
List<BlockRequirement> getRequirements(Object subject);
}
@@ -0,0 +1,44 @@
package hellfirepvp.beebetteratbees.client.requirements;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import net.minecraft.block.Block;
import net.minecraft.item.ItemStack;
import net.minecraftforge.oredict.OreDictionary;
import forestry.api.genetics.IMutationCondition;
import forestry.core.genetics.mutations.Mutation;
import forestry.core.genetics.mutations.MutationConditionRequiresResource;
import forestry.core.genetics.mutations.MutationConditionRequiresResourceOreDict;
public class MutationConditionRequirementProvider implements IBlockRequirementProvider {
@Override
public List<BlockRequirement> getRequirements(Object subject) {
if (!(subject instanceof Mutation)) return Collections.emptyList();
List<BlockRequirement> result = new ArrayList<>();
for (IMutationCondition condition : ((Mutation) subject).getMutationConditions()) {
if (condition == null) continue;
List<ItemStack> candidates = candidates(condition);
if (!candidates.isEmpty()) result.add(new BlockRequirement(candidates, "bbab.requirement.blocks"));
}
return result;
}
private List<ItemStack> candidates(IMutationCondition condition) {
if (condition instanceof MutationConditionRequiresResource) {
ItemStack required = ((MutationConditionRequiresResource) condition).getBlockRequired();
if (required == null || required.getItem() == null) return Collections.emptyList();
return RequirementStacks.fromBlock(Block.getBlockFromItem(required.getItem()), required.getItemDamage());
}
if (!(condition instanceof MutationConditionRequiresResourceOreDict)) return Collections.emptyList();
String oreName = OreDictionary
.getOreName(((MutationConditionRequiresResourceOreDict) condition).getOreDictId());
if (oreName == null || oreName.isEmpty()) return Collections.emptyList();
List<ItemStack> result = new ArrayList<>();
for (ItemStack stack : OreDictionary.getOres(oreName)) RequirementStacks.add(result, stack);
return result;
}
}
@@ -0,0 +1,37 @@
package hellfirepvp.beebetteratbees.client.requirements;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class RequirementLayout {
public static final int SLOT_SIZE = 18;
public static final int SLOT_PITCH = 20;
public static final int SLOT_GAP = 6;
public static final int GUI_WIDTH = 166;
public static final int MIN_RECIPE_HEIGHT = 65;
public static List<RequirementSlot> place(List<BlockRequirement> requirements, int beeX, int beeY) {
List<BlockRequirement> groups = new ArrayList<>();
for (BlockRequirement requirement : requirements)
if (requirement != null && !requirement.isEmpty()) groups.add(requirement);
if (groups.isEmpty()) return Collections.emptyList();
int width = SLOT_SIZE + (groups.size() - 1) * SLOT_PITCH;
int belowY = beeY + SLOT_SIZE + SLOT_GAP;
int centeredX = Math.max(0, Math.min(GUI_WIDTH - width, beeX - (width - SLOT_SIZE) / 2));
return placeRow(groups, centeredX, belowY);
}
private static List<RequirementSlot> placeRow(List<BlockRequirement> groups, int x, int y) {
int width = SLOT_SIZE + (groups.size() - 1) * SLOT_PITCH;
if (x < 0 || y < 0 || x + width > GUI_WIDTH) {
return Collections.emptyList();
}
List<RequirementSlot> result = new ArrayList<>();
for (int index = 0; index < groups.size(); index++) {
result.add(new RequirementSlot(groups.get(index), x + index * SLOT_PITCH, y));
}
return result;
}
}
@@ -0,0 +1,44 @@
package hellfirepvp.beebetteratbees.client.requirements;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import net.minecraft.item.ItemStack;
import hellfirepvp.beebetteratbees.common.BeeBetterAtBees;
public class RequirementResolvers {
private static final List<IBlockRequirementProvider> PROVIDERS = new ArrayList<>();
public static void register(IBlockRequirementProvider provider) {
if (provider != null && !PROVIDERS.contains(provider)) PROVIDERS.add(provider);
}
public static List<BlockRequirement> resolve(Object subject) {
List<BlockRequirement> result = new ArrayList<>();
if (subject == null) return Collections.emptyList();
for (IBlockRequirementProvider provider : PROVIDERS) {
try {
List<BlockRequirement> contributed = provider.getRequirements(subject);
if (contributed != null) for (BlockRequirement requirement : contributed) {
if (requirement != null && !requirement.isEmpty()) result.add(requirement);
}
} catch (Throwable error) {
BeeBetterAtBees.log.warn("Requirement provider failed for {}", subject, error);
}
}
return result;
}
public static boolean matches(Object subject, ItemStack ingredient) {
if (ingredient == null) return false;
for (BlockRequirement requirement : resolve(subject)) {
for (ItemStack candidate : requirement.getCandidates()) {
if (RequirementStacks.matches(candidate, ingredient)) return true;
}
}
return false;
}
}
@@ -0,0 +1,23 @@
package hellfirepvp.beebetteratbees.client.requirements;
import java.util.Collections;
import net.minecraft.util.StatCollector;
import codechicken.nei.PositionedStack;
public class RequirementSlot extends PositionedStack {
private final String label;
public RequirementSlot(BlockRequirement requirement, int x, int y) {
super(requirement.getCandidates(), x, y);
this.label = requirement.getLabel();
setTooltip(Collections.singletonList(StatCollector.translateToLocal(label)));
}
public String getLabel() {
return label;
}
}
@@ -0,0 +1,67 @@
package hellfirepvp.beebetteratbees.client.requirements;
import java.util.ArrayList;
import java.util.List;
import net.minecraft.block.Block;
import net.minecraft.init.Blocks;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraftforge.oredict.OreDictionary;
public class RequirementStacks {
public static List<ItemStack> fromBlock(Block block, int meta) {
List<ItemStack> result = new ArrayList<>();
if (block == null || block == Blocks.air) return result;
Item item = Item.getItemFromBlock(block);
if (item == null) return result;
if (meta == OreDictionary.WILDCARD_VALUE) {
addSubItems(result, item);
}
if (result.isEmpty()) add(result, new ItemStack(item, 1, meta == OreDictionary.WILDCARD_VALUE ? 0 : meta));
return result;
}
public static List<ItemStack> sanitize(List<ItemStack> stacks) {
List<ItemStack> result = new ArrayList<>();
if (stacks != null) for (ItemStack stack : stacks) add(result, stack);
return result;
}
public static void add(List<ItemStack> target, ItemStack stack) {
if (stack == null || stack.getItem() == null) return;
if (stack.getItemDamage() == OreDictionary.WILDCARD_VALUE) {
int sizeBeforeExpansion = target.size();
addSubItems(target, stack.getItem());
if (target.size() == sizeBeforeExpansion) addExact(target, stack.getItem(), 0);
return;
}
addExact(target, stack.getItem(), stack.getItemDamage());
}
public static boolean matches(ItemStack candidate, ItemStack ingredient) {
return candidate != null && ingredient != null
&& candidate.isItemEqual(ingredient)
&& ItemStack.areItemStackTagsEqual(candidate, ingredient);
}
public static void addSubItems(List<ItemStack> target, Item item) {
if (target == null || item == null) return;
List<ItemStack> variants = new ArrayList<>();
item.getSubItems(item, null, variants);
for (ItemStack variant : variants) {
if (variant != null && variant.getItem() != null
&& variant.getItemDamage() != OreDictionary.WILDCARD_VALUE) {
addExact(target, variant.getItem(), variant.getItemDamage());
}
}
}
private static void addExact(List<ItemStack> target, Item item, int meta) {
for (ItemStack existing : target) {
if (existing.getItem() == item && existing.getItemDamage() == meta) return;
}
target.add(new ItemStack(item, 1, meta));
}
}
@@ -1 +1,2 @@
bbab.gui.breedtree=Bee Breeding Tree
bbab.gui.breedtree=Bee Breeding Tree
bbab.requirement.blocks=Required Blocks
@@ -0,0 +1,2 @@
bbab.gui.breedtree=蜜蜂杂交树
bbab.requirement.blocks=需求方块