package choonster.testmod3.event; import choonster.testmod3.init.ModItems; import com.google.common.collect.ImmutableSet; import net.minecraft.entity.Entity; import net.minecraft.entity.item.EntityItem; import net.minecraft.init.Items; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import net.minecraft.util.EnumParticleTypes; import net.minecraft.util.math.AxisAlignedBB; import net.minecraft.world.World; import net.minecraft.world.WorldServer; import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; import net.minecraftforge.fml.common.gameevent.TickEvent; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.function.Predicate; import java.util.stream.Collectors; /** * Combines items in the world. * <p> * Uses {@link TickEvent.WorldTickEvent} and iterates through {@link World#loadedEntityList} to allow for all input items to be from vanilla or other mods. Creating a dedicated item with a custom entity to act as the controller of this effect would be more efficient. * <p> * Test for this thread: * http://www.minecraftforum.net/forums/mapping-and-modding/minecraft-mods/modification-development/2728653-better-way-to-check-for-entities-in-world * * @author Choonster */ @Mod.EventBusSubscriber public class ItemCombinationHandler { /** * The input items. */ private static final Set<Item> INPUTS = ImmutableSet.of(Items.BONE, Items.BOOK, Items.FEATHER); /** * The output item. */ private static final ItemStack OUTPUT = new ItemStack(ModItems.GUN); private ItemCombinationHandler() { } @SubscribeEvent public static void onWorldTick(final TickEvent.WorldTickEvent event) { final World world = event.world; // If this is the END phase on the server, if (event.phase == TickEvent.Phase.END && !world.isRemote) { // Handle each loaded EntityItem with an input item world.loadedEntityList.stream() .filter(isMatchingItemEntity(INPUTS)) .map(entity -> (EntityItem) entity) .collect(Collectors.toList()) .forEach(ItemCombinationHandler::handleEntity); } } /** * Handles the combination effect for an {@link EntityItem}. * * @param entityItem The item entity */ private static void handleEntity(final EntityItem entityItem) { // If the item entity is dead, do nothing if (!entityItem.isEntityAlive()) return; final World world = entityItem.getEntityWorld(); final Set<Item> remainingInputs = new HashSet<>(INPUTS); // Create a mutable copy of the input set to track which items have been found final List<EntityItem> matchingEntityItems = new ArrayList<>(); // Create a list to track the item entities containing the input items remainingInputs.remove(entityItem.getEntityItem().getItem()); matchingEntityItems.add(entityItem); // Find all other item entities with input items within 3 blocks final AxisAlignedBB axisAlignedBB = entityItem.getEntityBoundingBox().expandXyz(3); final List<Entity> nearbyEntityItems = world.getEntitiesInAABBexcluding(entityItem, axisAlignedBB, isMatchingItemEntity(remainingInputs)::test); // For each nearby item entity nearbyEntityItems.forEach(nearbyEntity -> { final EntityItem nearbyEntityItem = (EntityItem) nearbyEntity; if (remainingInputs.remove(nearbyEntityItem.getEntityItem().getItem())) { // If the entity's item is a remaining input, matchingEntityItems.add(nearbyEntityItem); // Add it to the list of matching item entities if (remainingInputs.isEmpty()) { // If all inputs have been found, // Spawn the output item at the first item's position final double x = entityItem.posX, y = entityItem.posY, z = entityItem.posZ; final EntityItem outputEntityItem = new EntityItem(world, x, y, z, OUTPUT.copy()); world.spawnEntity(outputEntityItem); ((WorldServer) world).spawnParticle(EnumParticleTypes.SMOKE_LARGE, x + 0.5, y + 1.0, z + 0.5, 1, 0, 0, 0, 0, new int[0]); // Consume one item from each matching entity matchingEntityItems.forEach(matchingEntityItem -> { final ItemStack itemStack = matchingEntityItem.getEntityItem(); itemStack.shrink(1); if (itemStack.isEmpty()) { matchingEntityItem.setDead(); } }); } } }); } /** * Returns a predicate that determines whether the entity is a living {@link EntityItem} whose {@link Item} is contained in the {@link Set}. * * @param items The set of items to match * @return The predicate */ private static Predicate<Entity> isMatchingItemEntity(Set<Item> items) { return entity -> entity.isEntityAlive() && entity instanceof EntityItem && items.contains(((EntityItem) entity).getEntityItem().getItem()); } }