CreateMod/src/main/java/com/simibubi/create/content/logistics/item/filter/ItemAttribute.java
simibubi 06ca8b0d1c What's inside?
- Blueprint crafting overlay now attempts to show a list of items matching an ingredient filter in the respective slot
- Fixed JEI prematurely transferring the blueprint recipe when viewed
2021-06-17 00:54:48 +02:00

369 lines
11 KiB
Java

package com.simibubi.create.content.logistics.item.filter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.function.BiPredicate;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
import com.simibubi.create.AllRecipeTypes;
import com.simibubi.create.content.contraptions.processing.InWorldProcessing;
import com.simibubi.create.content.logistics.item.filter.attribute.BookAuthorAttribute;
import com.simibubi.create.content.logistics.item.filter.attribute.BookCopyAttribute;
import com.simibubi.create.content.logistics.item.filter.attribute.ColorAttribute;
import com.simibubi.create.content.logistics.item.filter.attribute.EnchantAttribute;
import com.simibubi.create.content.logistics.item.filter.attribute.FluidContentsAttribute;
import com.simibubi.create.content.logistics.item.filter.attribute.ItemNameAttribute;
import com.simibubi.create.content.logistics.item.filter.attribute.astralsorcery.AstralSorceryAmuletAttribute;
import com.simibubi.create.content.logistics.item.filter.attribute.astralsorcery.AstralSorceryAttunementAttribute;
import com.simibubi.create.content.logistics.item.filter.attribute.astralsorcery.AstralSorceryCrystalAttribute;
import com.simibubi.create.content.logistics.item.filter.attribute.astralsorcery.AstralSorceryPerkGemAttribute;
import com.simibubi.create.foundation.utility.Lang;
import net.minecraft.enchantment.EnchantmentHelper;
import net.minecraft.inventory.IInventory;
import net.minecraft.item.BlockItem;
import net.minecraft.item.Item;
import net.minecraft.item.ItemGroup;
import net.minecraft.item.ItemStack;
import net.minecraft.item.crafting.IRecipe;
import net.minecraft.item.crafting.IRecipeType;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.tileentity.AbstractFurnaceTileEntity;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.text.TranslationTextComponent;
import net.minecraft.world.World;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.fluids.capability.CapabilityFluidHandler;
import net.minecraftforge.fml.ModContainer;
import net.minecraftforge.fml.ModList;
import net.minecraftforge.forgespi.language.IModInfo;
import net.minecraftforge.items.ItemStackHandler;
import net.minecraftforge.items.wrapper.RecipeWrapper;
public interface ItemAttribute {
static List<ItemAttribute> types = new ArrayList<>();
static ItemAttribute standard = register(StandardTraits.DUMMY);
static ItemAttribute inTag = register(new InTag(new ResourceLocation("dummy")));
static ItemAttribute inItemGroup = register(new InItemGroup(ItemGroup.MISC));
static ItemAttribute addedBy = register(new InItemGroup.AddedBy("dummy"));
static ItemAttribute hasEnchant = register(EnchantAttribute.EMPTY);
static ItemAttribute hasColor = register(ColorAttribute.EMPTY);
static ItemAttribute hasFluid = register(FluidContentsAttribute.EMPTY);
static ItemAttribute hasName = register(new ItemNameAttribute("dummy"));
static ItemAttribute astralAmulet = register(new AstralSorceryAmuletAttribute("dummy", -1));
static ItemAttribute astralAttunement = register(new AstralSorceryAttunementAttribute("dummy"));
static ItemAttribute astralCrystal = register(new AstralSorceryCrystalAttribute("dummy"));
static ItemAttribute astralPerkGem = register(new AstralSorceryPerkGemAttribute("dummy"));
static ItemAttribute bookAuthor = register(new BookAuthorAttribute("dummy"));
static ItemAttribute bookCopy = register(new BookCopyAttribute(-1));
static ItemAttribute register(ItemAttribute attributeType) {
types.add(attributeType);
return attributeType;
}
default boolean appliesTo(ItemStack stack, World world) {
return appliesTo(stack);
}
boolean appliesTo(ItemStack stack);
default List<ItemAttribute> listAttributesOf(ItemStack stack, World world) {
return listAttributesOf(stack);
}
public List<ItemAttribute> listAttributesOf(ItemStack stack);
public String getTranslationKey();
void writeNBT(CompoundNBT nbt);
ItemAttribute readNBT(CompoundNBT nbt);
public default void serializeNBT(CompoundNBT nbt) {
CompoundNBT compound = new CompoundNBT();
writeNBT(compound);
nbt.put(getNBTKey(), compound);
}
public static ItemAttribute fromNBT(CompoundNBT nbt) {
for (ItemAttribute itemAttribute : types) {
if (!itemAttribute.canRead(nbt))
continue;
return itemAttribute.readNBT(nbt.getCompound(itemAttribute.getNBTKey()));
}
return null;
}
default Object[] getTranslationParameters() {
return new String[0];
}
default boolean canRead(CompoundNBT nbt) {
return nbt.contains(getNBTKey());
}
default String getNBTKey() {
return getTranslationKey();
}
@OnlyIn(value = Dist.CLIENT)
default TranslationTextComponent format(boolean inverted) {
return Lang.translate("item_attributes." + getTranslationKey() + (inverted ? ".inverted" : ""),
getTranslationParameters());
}
public static enum StandardTraits implements ItemAttribute {
DUMMY(s -> false),
PLACEABLE(s -> s.getItem() instanceof BlockItem),
CONSUMABLE(ItemStack::isFood),
FLUID_CONTAINER(s -> s.getCapability(CapabilityFluidHandler.FLUID_HANDLER_ITEM_CAPABILITY).isPresent()),
ENCHANTED(ItemStack::isEnchanted),
RENAMED(ItemStack::hasDisplayName),
DAMAGED(ItemStack::isDamaged),
BADLY_DAMAGED(s -> s.isDamaged() && s.getDamage() / s.getMaxDamage() > 3 / 4f),
NOT_STACKABLE(((Predicate<ItemStack>) ItemStack::isStackable).negate()),
EQUIPABLE(s -> s.getEquipmentSlot() != null),
MAX_ENCHANTED(StandardTraits::maxEnchanted),
FURNACE_FUEL(AbstractFurnaceTileEntity::isFuel),
WASHABLE(InWorldProcessing::isWashable),
CRUSHABLE((s, w) -> testRecipe(s, w, AllRecipeTypes.CRUSHING.getType())
|| testRecipe(s, w, AllRecipeTypes.MILLING.getType())),
SMELTABLE((s, w) -> testRecipe(s, w, IRecipeType.SMELTING)),
SMOKABLE((s, w) -> testRecipe(s, w, IRecipeType.SMOKING)),
BLASTABLE((s, w) -> testRecipe(s, w, IRecipeType.BLASTING));
private static final RecipeWrapper RECIPE_WRAPPER = new RecipeWrapper(new ItemStackHandler(1));
private Predicate<ItemStack> test;
private BiPredicate<ItemStack, World> testWithWorld;
private StandardTraits(Predicate<ItemStack> test) {
this.test = test;
}
private static boolean testRecipe(ItemStack s, World w, IRecipeType<? extends IRecipe<IInventory>> type) {
RECIPE_WRAPPER.setInventorySlotContents(0, s.copy());
return w.getRecipeManager()
.getRecipe(type, RECIPE_WRAPPER, w)
.isPresent();
}
private static boolean maxEnchanted(ItemStack s) {
return EnchantmentHelper.getEnchantments(s)
.entrySet()
.stream()
.anyMatch(e -> e.getKey().getMaxLevel() <= e.getValue());
}
private StandardTraits(BiPredicate<ItemStack, World> test) {
this.testWithWorld = test;
}
@Override
public boolean appliesTo(ItemStack stack, World world) {
if (testWithWorld != null)
return testWithWorld.test(stack, world);
return appliesTo(stack);
}
@Override
public boolean appliesTo(ItemStack stack) {
return test.test(stack);
}
@Override
public List<ItemAttribute> listAttributesOf(ItemStack stack, World world) {
List<ItemAttribute> attributes = new ArrayList<>();
for (StandardTraits trait : values())
if (trait.appliesTo(stack, world))
attributes.add(trait);
return attributes;
}
@Override
public List<ItemAttribute> listAttributesOf(ItemStack stack) {
return null;
}
@Override
public String getTranslationKey() {
return Lang.asId(name());
}
@Override
public String getNBTKey() {
return "standard_trait";
}
@Override
public void writeNBT(CompoundNBT nbt) {
nbt.putBoolean(name(), true);
}
@Override
public ItemAttribute readNBT(CompoundNBT nbt) {
for (StandardTraits trait : values())
if (nbt.contains(trait.name()))
return trait;
return null;
}
}
public static class InTag implements ItemAttribute {
public ResourceLocation tagName;
public InTag(ResourceLocation tagName) {
this.tagName = tagName;
}
@Override
public boolean appliesTo(ItemStack stack) {
return stack.getItem()
.getTags()
.contains(tagName);
}
@Override
public List<ItemAttribute> listAttributesOf(ItemStack stack) {
return stack.getItem()
.getTags()
.stream()
.map(InTag::new)
.collect(Collectors.toList());
}
@Override
public String getTranslationKey() {
return "in_tag";
}
@Override
public Object[] getTranslationParameters() {
return new Object[] { "#" + tagName.toString() };
}
@Override
public void writeNBT(CompoundNBT nbt) {
nbt.putString("space", tagName.getNamespace());
nbt.putString("path", tagName.getPath());
}
@Override
public ItemAttribute readNBT(CompoundNBT nbt) {
return new InTag(new ResourceLocation(nbt.getString("space"), nbt.getString("path")));
}
}
public static class InItemGroup implements ItemAttribute {
private ItemGroup group;
public InItemGroup(ItemGroup group) {
this.group = group;
}
@Override
public boolean appliesTo(ItemStack stack) {
Item item = stack.getItem();
return item.getGroup() == group;
}
@Override
public List<ItemAttribute> listAttributesOf(ItemStack stack) {
ItemGroup group = stack.getItem()
.getGroup();
return group == null ? Collections.emptyList() : Arrays.asList(new InItemGroup(group));
}
@Override
public String getTranslationKey() {
return "in_item_group";
}
@Override
@OnlyIn(value = Dist.CLIENT)
public TranslationTextComponent format(boolean inverted) {
return Lang.translate("item_attributes." + getTranslationKey() + (inverted ? ".inverted" : ""),
group.getTranslationKey());
}
@Override
public void writeNBT(CompoundNBT nbt) {
nbt.putString("path", group.getPath());
}
@Override
public ItemAttribute readNBT(CompoundNBT nbt) {
String readPath = nbt.getString("path");
for (ItemGroup group : ItemGroup.GROUPS)
if (group.getPath()
.equals(readPath))
return new InItemGroup(group);
return null;
}
}
public static class AddedBy implements ItemAttribute {
private String modId;
public AddedBy(String modId) {
this.modId = modId;
}
@Override
public boolean appliesTo(ItemStack stack) {
return modId.equals(stack.getItem()
.getCreatorModId(stack));
}
@Override
public List<ItemAttribute> listAttributesOf(ItemStack stack) {
String id = stack.getItem()
.getCreatorModId(stack);
return id == null ? Collections.emptyList() : Arrays.asList(new AddedBy(id));
}
@Override
public String getTranslationKey() {
return "added_by";
}
@Override
public Object[] getTranslationParameters() {
Optional<? extends ModContainer> modContainerById = ModList.get()
.getModContainerById(modId);
String name = modContainerById.map(ModContainer::getModInfo)
.map(IModInfo::getDisplayName)
.orElse(StringUtils.capitalize(modId));
return new Object[] { name };
}
@Override
public void writeNBT(CompoundNBT nbt) {
nbt.putString("id", modId);
}
@Override
public ItemAttribute readNBT(CompoundNBT nbt) {
return new AddedBy(nbt.getString("id"));
}
}
}