Skip to content

Commit

Permalink
Add anvil recipes, Close #1381
Browse files Browse the repository at this point in the history
  • Loading branch information
shedaniel committed Apr 16, 2024
1 parent da47c76 commit 6cc0322
Show file tree
Hide file tree
Showing 4 changed files with 137 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@
import net.minecraft.world.item.alchemy.PotionBrewing;
import net.minecraft.world.item.alchemy.PotionUtils;
import net.minecraft.world.item.crafting.*;
import net.minecraft.world.item.enchantment.EnchantmentInstance;
import net.minecraft.world.level.GameType;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.ComposterBlock;
Expand All @@ -104,11 +105,13 @@
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.material.Fluid;
import net.minecraft.world.level.material.FluidState;
import org.apache.commons.lang3.tuple.Pair;
import org.jetbrains.annotations.ApiStatus;

import java.util.*;
import java.util.function.Supplier;
import java.util.function.UnaryOperator;
import java.util.stream.IntStream;
import java.util.stream.Stream;

@Environment(EnvType.CLIENT)
Expand Down Expand Up @@ -355,6 +358,66 @@ public void registerDisplays(DisplayRegistry registry) {
registerForgePotions(registry, this);
}

for (Item item : Registry.ITEM) {
ItemStack stack = item.getDefaultInstance();
if (!stack.isDamageableItem()) continue;
EntryIngredient repairMaterialBase = null;
if (item instanceof TieredItem tieredItem) {
Tier tier = tieredItem.getTier();
repairMaterialBase = EntryIngredients.ofIngredient(tier.getRepairIngredient());
} else if (item instanceof ArmorItem armorItem) {
ArmorMaterial material = armorItem.getMaterial();
repairMaterialBase = EntryIngredients.ofIngredient(material.getRepairIngredient());
} else if (item instanceof ShieldItem shieldItem) {
repairMaterialBase = EntryIngredients.ofItemTag(ItemTags.PLANKS);
repairMaterialBase.filter(s -> shieldItem.isValidRepairItem(stack, s.castValue()));
} else if (item instanceof ElytraItem elytraItem) {
repairMaterialBase = EntryIngredients.of(Items.PHANTOM_MEMBRANE);
repairMaterialBase.filter(s -> elytraItem.isValidRepairItem(stack, s.castValue()));
}
if (repairMaterialBase == null || repairMaterialBase.isEmpty()) continue;
for (int[] i = {1}; i[0] <= 4; i[0]++) {
ItemStack baseStack = item.getDefaultInstance();
int toRepair = i[0] == 4 ? item.getMaxDamage() : baseStack.getMaxDamage() / 4 * i[0];
baseStack.setDamageValue(toRepair);
EntryIngredient repairMaterial = repairMaterialBase.map(s -> {
EntryStack<?> newStack = s.copy();
newStack.<ItemStack>castValue().setCount(i[0]);
return newStack;
});
Optional<Pair<ItemStack, Integer>> output = DefaultAnvilDisplay.calculateOutput(baseStack, repairMaterial.get(0).castValue());
if (output.isEmpty()) continue;
registry.add(new DefaultAnvilDisplay(List.of(EntryIngredients.of(baseStack), repairMaterial),
Collections.singletonList(EntryIngredients.of(output.get().getLeft())), Optional.empty(), OptionalInt.of(output.get().getRight())));
}
}
List<Pair<EnchantmentInstance, ItemStack>> enchantmentBooks = Registry.ENCHANTMENT.stream()
.flatMap(enchantment -> {
if (enchantment.getMaxLevel() - enchantment.getMinLevel() >= 10) {
return IntStream.of(enchantment.getMinLevel(), enchantment.getMaxLevel())
.mapToObj(lvl -> new EnchantmentInstance(enchantment, lvl));
} else {
return IntStream.rangeClosed(enchantment.getMinLevel(), enchantment.getMaxLevel())
.mapToObj(lvl -> new EnchantmentInstance(enchantment, lvl));
}
})
.map(instance -> {
return Pair.of(instance, EnchantedBookItem.createForEnchantment(instance));
})
.toList();
EntryRegistry.getInstance().getEntryStacks().forEach(stack -> {
if (stack.getType() != VanillaEntryTypes.ITEM) return;
ItemStack itemStack = stack.castValue();
if (!itemStack.isEnchantable()) return;
for (Pair<EnchantmentInstance, ItemStack> pair : enchantmentBooks) {
if (!pair.getKey().enchantment.canEnchant(itemStack)) continue;
Optional<Pair<ItemStack, Integer>> output = DefaultAnvilDisplay.calculateOutput(itemStack, pair.getValue());
if (output.isEmpty()) continue;
registry.add(new DefaultAnvilDisplay(List.of(EntryIngredients.of(itemStack), EntryIngredients.of(pair.getValue())),
Collections.singletonList(EntryIngredients.of(output.get().getLeft())), Optional.empty(), OptionalInt.of(output.get().getRight())));
}
});

for (Registry<?> reg : Registry.REGISTRY) {
reg.getTags().forEach(tagPair -> registry.add(tagPair.getFirst()));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@
import me.shedaniel.rei.api.common.util.EntryStacks;
import me.shedaniel.rei.plugin.common.BuiltinPlugin;
import me.shedaniel.rei.plugin.common.displays.anvil.DefaultAnvilDisplay;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.Font;
import net.minecraft.client.gui.GuiComponent;
import net.minecraft.network.chat.Component;
import net.minecraft.world.level.block.Blocks;

Expand All @@ -57,19 +60,28 @@ public Renderer getIcon() {

@Override
public List<Widget> setupDisplay(DefaultAnvilDisplay display, Rectangle bounds) {
Point startPoint = new Point(bounds.getCenterX() - 31, bounds.getCenterY() - 13);
Point startPoint = new Point(bounds.getCenterX() - 31, bounds.getCenterY() - (display.getCost().isPresent() ? 20 : 13));
List<Widget> widgets = Lists.newArrayList();
widgets.add(Widgets.createRecipeBase(bounds));
widgets.add(Widgets.createArrow(new Point(startPoint.x + 27, startPoint.y + 4)));
widgets.add(Widgets.createResultSlotBackground(new Point(startPoint.x + 61, startPoint.y + 5)));
widgets.add(Widgets.createSlot(new Point(startPoint.x + 4 - 22, startPoint.y + 5)).entries(display.getInputEntries().get(0)).markInput());
widgets.add(Widgets.createSlot(new Point(startPoint.x + 4, startPoint.y + 5)).entries(display.getInputEntries().get(1)).markInput());
widgets.add(Widgets.createSlot(new Point(startPoint.x + 61, startPoint.y + 5)).entries(display.getOutputEntries().get(0)).disableBackground().markOutput());
if (display.getCost().isPresent()) {
widgets.add(Widgets.createDrawableWidget((helper, matrices, mouseX, mouseY, delta) -> {
Font font = Minecraft.getInstance().font;
Component component = Component.translatable("container.repair.cost", display.getCost().getAsInt());
int x = startPoint.x + 102 - font.width(component) - 2;
GuiComponent.fill(matrices, x - 2, startPoint.y + 28, startPoint.x + 102, startPoint.y + 28 + 12, 0x4f000000);
font.drawShadow(matrices, component, x, startPoint.y + 28 + 2, 0x80ff20);
}));
}
return widgets;
}

@Override
public int getDisplayHeight() {
return 36;
return 48;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,19 +28,26 @@
import org.jetbrains.annotations.Nullable;

import java.util.List;
import java.util.OptionalInt;

public class AnvilRecipe {
@Nullable
private final ResourceLocation id;
private final List<ItemStack> leftInput;
private final List<ItemStack> rightInputs;
private final List<ItemStack> outputs;
private final OptionalInt cost;

public AnvilRecipe(@Nullable ResourceLocation id, List<ItemStack> leftInput, List<ItemStack> rightInputs, List<ItemStack> outputs) {
this(id, leftInput, rightInputs, outputs, OptionalInt.empty());
}

public AnvilRecipe(@Nullable ResourceLocation id, List<ItemStack> leftInput, List<ItemStack> rightInputs, List<ItemStack> outputs, OptionalInt cost) {
this.id = id;
this.leftInput = leftInput;
this.rightInputs = rightInputs;
this.outputs = outputs;
this.cost = cost;
}

public ResourceLocation getId() {
Expand All @@ -58,4 +65,8 @@ public List<ItemStack> getRightInputs() {
public List<ItemStack> getOutputs() {
return outputs;
}

public OptionalInt getCost() {
return cost;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,35 +28,78 @@
import me.shedaniel.rei.api.common.entry.EntryIngredient;
import me.shedaniel.rei.api.common.util.EntryIngredients;
import me.shedaniel.rei.plugin.common.BuiltinPlugin;
import net.minecraft.client.Minecraft;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.inventory.AnvilMenu;
import net.minecraft.world.item.ItemStack;
import org.apache.commons.lang3.tuple.Pair;
import org.jetbrains.annotations.ApiStatus;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.*;

public class DefaultAnvilDisplay extends BasicDisplay {
private final OptionalInt cost;

public DefaultAnvilDisplay(AnvilRecipe recipe) {
this(
Arrays.asList(
EntryIngredients.ofItemStacks(recipe.getLeftInput()),
EntryIngredients.ofItemStacks(recipe.getRightInputs())
),
Collections.singletonList(EntryIngredients.ofItemStacks(recipe.getOutputs())),
Optional.ofNullable(recipe.getId())
Optional.ofNullable(recipe.getId()),
recipe.getCost()
);
}

public DefaultAnvilDisplay(List<EntryIngredient> inputs, List<EntryIngredient> outputs, Optional<ResourceLocation> location) {
this(inputs, outputs, location, OptionalInt.empty());
}

public DefaultAnvilDisplay(List<EntryIngredient> inputs, List<EntryIngredient> outputs, Optional<ResourceLocation> location, CompoundTag tag) {
this(inputs, outputs, location, tag.contains("Cost") ? OptionalInt.of(tag.getInt("Cost")) : OptionalInt.empty());
}

public DefaultAnvilDisplay(List<EntryIngredient> inputs, List<EntryIngredient> outputs, Optional<ResourceLocation> location, OptionalInt cost) {
super(inputs, outputs, location);
this.cost = cost;
}

@Override
public CategoryIdentifier<?> getCategoryIdentifier() {
return BuiltinPlugin.ANVIL;
}

public OptionalInt getCost() {
return cost;
}

public static BasicDisplay.Serializer<DefaultAnvilDisplay> serializer() {
return BasicDisplay.Serializer.ofSimple(DefaultAnvilDisplay::new);
return BasicDisplay.Serializer.of(DefaultAnvilDisplay::new, (display, tag) -> {
if (display.getCost().isPresent()) {
tag.putInt("Cost", display.getCost().getAsInt());
}
});
}

@ApiStatus.Experimental
@ApiStatus.Internal
public static Optional<Pair<ItemStack, Integer>> calculateOutput(ItemStack left, ItemStack right) {
try {
if (Minecraft.getInstance().player == null) return Optional.empty();
AnvilMenu menu = new AnvilMenu(0, new Inventory(Minecraft.getInstance().player));
menu.setItem(0, menu.incrementStateId(), left);
menu.setItem(1, menu.incrementStateId(), right);
ItemStack output = menu.getSlot(2).getItem().copy();
if (!output.isEmpty()) {
return Optional.of(Pair.of(output, menu.getCost()));
} else {
return Optional.empty();
}
} catch (Throwable ignored) {
return Optional.empty();
}
}
}

0 comments on commit 6cc0322

Please sign in to comment.