Java tutorial
/******************************************************************************* * The MIT License (MIT) * * Copyright (C) 2014-2018 Sam Bassett (aka Lothrazar) * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. ******************************************************************************/ package com.lothrazar.cyclicmagic.util; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Map.Entry; import com.google.common.collect.UnmodifiableIterator; import com.lothrazar.cyclicmagic.ModCyclic; import net.minecraft.block.Block; import net.minecraft.block.properties.IProperty; import net.minecraft.block.properties.PropertyBool; import net.minecraft.block.properties.PropertyInteger; import net.minecraft.block.state.IBlockState; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import net.minecraft.util.NonNullList; import net.minecraft.util.ResourceLocation; import net.minecraft.util.math.BlockPos; import net.minecraft.world.World; import net.minecraftforge.common.config.Configuration; public class UtilHarvester { private static final int FORTUNE = 5; private static final String AGE = "age"; static final boolean tryRemoveOneSeed = true; //a break config means ignore age and go right for it // a harvest config means check AGE private static NonNullList<String> breakGetDrops; private static NonNullList<String> breakSilkTouch; private static NonNullList<String> blockIgnore; private static NonNullList<String> harvestReflectionRegrow; private static NonNullList<String> harvestGetDropsDeprecated; private static NonNullList<String> breakGetDropsDeprecated; private static NonNullList<String> blocksBreakAboveIfMatching; private static NonNullList<String> blocksBreakAboveIfMatchingAfterHarvest; private static Map<String, Integer> harvestCustomMaxAge; private static Map<String, String> modsThatDontUseAge = new HashMap<String, String>(); private static Map<String, String> useBooleanProperty = new HashMap<String, String>(); private static NonNullList<String> blocksDoNotRemoveSeeds; public static void syncConfig(Configuration config) { String category = Const.ConfigCategory.modpackMisc; String[] deflist = new String[] { "terraqueous:pergola", "minecraft:*_stem", "croparia:stem_*", "rustic:grape_stem" }; String[] blacklist = config.getStringList("HarvesterBlacklist", category, deflist, "Crops & bushes that are blocked from harvesting (Garden Scythe and Harvester). A star is for a wildcard"); //TODO: config it after its decided? maybe? maybe not? /* @formatter:off */ blockIgnore = NonNullList.from("", blacklist); breakGetDrops = NonNullList.from("", "minecraft:pumpkin", "croparia:block_plant_*", "croparia:block_cane_*", "extrautils2:redorchid"); breakSilkTouch = NonNullList.from("", "minecraft:melon_block"); //there are two versions of the getDrops method in block class //and re check it for harvest vs break harvestGetDropsDeprecated = NonNullList.from("", "rustic:tomato_crop", "rustic:chili_crop", "harvestcraft:pamapple", "harvestcraft:pamalmond", "harvestcraft:pamapricot", "harvestcraft:pamavocado", "harvestcraft:pamcashew", "harvestcraft:pamcherry", "harvestcraft:pamchestnut", "harvestcraft:pamcoconut", "harvestcraft:pamdate", "harvestcraft:pamdragonfruit", "harvestcraft:pamdurian", "harvestcraft:pamfig", "harvestcraft:pamgooseberry", "harvestcraft:pamlemon", "harvestcraft:pamlime", "harvestcraft:pammango", "harvestcraft:pamnutmeg", "harvestcraft:pamolive", "harvestcraft:pamorange", "harvestcraft:pampapaya", "harvestcraft:pampeach", "harvestcraft:pampear", "harvestcraft:pampecan", "harvestcraft:pampeppercorn", "harvestcraft:pampersimmon", "harvestcraft:pampistachio", "harvestcraft:pamplum", "harvestcraft:pampomegranate", "harvestcraft:pamstarfruit", "harvestcraft:pamvanillabean", "harvestcraft:pamwalnut", "harvestcraft:pamspiderweb", "harvestcraft:pamcinnamon", "harvestcraft:pammaple", "harvestcraft:pampaperbark"); harvestReflectionRegrow = NonNullList.from("", "natura:overworld_berrybush_*", "natura:overworld_berrybush_blackberry", "natura:overworld_berrybush_raspberry", "natura:overworld_berrybush_maloberry", "natura:nether_berrybush_duskberry", "natura:nether_berrybush_stingberry", "natura:nether_berrybush_skyberry", "natura:nether_berrybush_blightberry", "natura:nether_berrybush_duskberry"); breakGetDropsDeprecated = NonNullList.from("", "attaineddrops2:bulb"); blocksBreakAboveIfMatching = NonNullList.from("", "immersiveengineering:hemp", "minecraft:reeds", "minecraft:cactus"); blocksBreakAboveIfMatchingAfterHarvest = NonNullList.from("", "simplecorn:corn"); blocksDoNotRemoveSeeds = NonNullList.from("", "plants2:crop_1"); useBooleanProperty.put("rustic:grape_leaves", "grapes"); modsThatDontUseAge.put("rustic:leaves_apple", "apple_age"); modsThatDontUseAge.put("extrautils2:enderlilly", "growth"); harvestCustomMaxAge = new HashMap<String, Integer>(); //max metadata is 11, but 9 is the lowest level when full grown //its a 3high multiblock harvestCustomMaxAge.put("simplecorn:corn", 9); /* @formatter:on */ } private static boolean doNotRemoveSeeds(ResourceLocation blockId) { return UtilString.isInList(blocksDoNotRemoveSeeds, blockId); } private static boolean isHarvestReflectionRegrow(ResourceLocation blockId) { return UtilString.isInList(harvestReflectionRegrow, blockId); } private static boolean isIgnored(ResourceLocation blockId) { return UtilString.isInList(blockIgnore, blockId); } private static boolean isBreakGetDrops(ResourceLocation blockId) { return UtilString.isInList(breakGetDrops, blockId); } private static boolean isBreakGetDropsDeprec(ResourceLocation blockId) { return UtilString.isInList(breakGetDropsDeprecated, blockId); } private static boolean isSimpleSilktouch(ResourceLocation blockId) { return UtilString.isInList(breakSilkTouch, blockId); } private static boolean isHarvestingGetDropsOld(ResourceLocation blockId) { return UtilString.isInList(harvestGetDropsDeprecated, blockId); } private static boolean isBreakAboveIfMatching(ResourceLocation blockId) { return UtilString.isInList(blocksBreakAboveIfMatching, blockId); } private static boolean isBreakAboveIfMatchingAfterHarvest(ResourceLocation blockId) { return UtilString.isInList(blocksBreakAboveIfMatchingAfterHarvest, blockId); } private static boolean doesBlockMatch(World world, Block blockCheck, BlockPos pos) { return world.getBlockState(pos).getBlock().equals(blockCheck); } private static PropertyBool getBoolProperty(IBlockState blockState, String property) { UnmodifiableIterator<Entry<IProperty<?>, Comparable<?>>> unmodifiableiterator = blockState.getProperties() .entrySet().iterator(); while (unmodifiableiterator.hasNext()) { // Entry<IProperty<?>, Comparable<?>> entry = unmodifiableiterator.next(); IProperty<?> iproperty = unmodifiableiterator.next().getKey(); if (iproperty instanceof PropertyBool && iproperty.getName().equals(property)) { return (PropertyBool) iproperty; } } return null; } private static PropertyInteger getAgeProperty(IBlockState blockState, ResourceLocation blockId) { UnmodifiableIterator<Entry<IProperty<?>, Comparable<?>>> unmodifiableiterator = blockState.getProperties() .entrySet().iterator(); while (unmodifiableiterator.hasNext()) { Entry<IProperty<?>, Comparable<?>> entry = unmodifiableiterator.next(); IProperty<?> iproperty = entry.getKey(); if (iproperty.getName() == null) { continue; } if (iproperty.getName().equals(AGE) && iproperty instanceof PropertyInteger) { return (PropertyInteger) iproperty; } else if (modsThatDontUseAge.containsKey(blockId.toString()) && iproperty.getName().equals(modsThatDontUseAge.get(blockId.toString())) && iproperty instanceof PropertyInteger) { return (PropertyInteger) iproperty; } } return null; } @SuppressWarnings("deprecation") public static NonNullList<ItemStack> harvestSingle(World world, BlockPos posCurrent) { final NonNullList<ItemStack> drops = NonNullList.create(); if (world.isAirBlock(posCurrent)) { return drops; } IBlockState blockState = world.getBlockState(posCurrent); Block blockCheck = blockState.getBlock(); ResourceLocation blockId = blockCheck.getRegistryName(); if (isIgnored(blockId)) { return drops; } if (isBreakGetDrops(blockId)) { blockCheck.getDrops(drops, world, posCurrent, blockState, FORTUNE); world.setBlockToAir(posCurrent); return drops; } if (isBreakGetDropsDeprec(blockId)) { drops.addAll(blockCheck.getDrops(world, posCurrent, blockState, FORTUNE)); world.setBlockToAir(posCurrent); return drops; } if (isSimpleSilktouch(blockId)) { drops.add(new ItemStack(blockCheck)); world.setBlockToAir(posCurrent); return drops; } if (isBreakAboveIfMatching(blockId) && doesBlockMatch(world, blockCheck, posCurrent.up())) { blockCheck.getDrops(drops, world, posCurrent, world.getBlockState(posCurrent.up()), FORTUNE); if (doesBlockMatch(world, blockCheck, posCurrent.up(2))) { blockCheck.getDrops(drops, world, posCurrent, world.getBlockState(posCurrent.up(2)), FORTUNE); world.destroyBlock(posCurrent.up(2), false);//also break this one too - thanks darkosto } world.destroyBlock(posCurrent.up(), false); return drops; } //boolean hasberry type of flag harvests if (useBooleanProperty.containsKey(blockId)) { String property = useBooleanProperty.get(blockId); PropertyBool propFlag = getBoolProperty(blockState, property); if (blockState.getValue(propFlag)) { blockCheck.getDrops(drops, world, posCurrent, blockState, FORTUNE); if (drops.size() == 0)//use deprecated only if main is offline drops.addAll(blockCheck.getDrops(world, posCurrent, blockState, FORTUNE)); // and then reset to off world.setBlockState(posCurrent, blockState.withProperty(propFlag, false)); } } //age growth PropertyInteger propInt = getAgeProperty(blockState, blockId); if (propInt != null) { int currentAge = blockState.getValue(propInt); int minAge = Collections.min(propInt.getAllowedValues()); int maxAge = Collections.max(propInt.getAllowedValues()); if (harvestCustomMaxAge.containsKey(blockId.toString())) { maxAge = harvestCustomMaxAge.get(blockId.toString()); } if (minAge == maxAge || currentAge < maxAge) { //degenerate edge case: either this was made wrong OR its not meant to grow //like a stem or log or something; return drops; } if (isHarvestReflectionRegrow(blockId)) { // ModCyclic.logger.log("[harvest] isHarvestReflectionRegrow " + blockId); Object toDrop = UtilReflection.getFirstPrivate(blockCheck, ItemStack.class); if (toDrop != null) { ItemStack crop = (ItemStack) toDrop; if (crop.isEmpty() == false) { drops.add(crop.copy()); //regrow : so only do -1, not full reset world.setBlockState(posCurrent, blockState.withProperty(propInt, maxAge - 1)); return drops; } } } //first get the drops if (isHarvestingGetDropsOld(blockId)) { // ModCyclic.logger.log("[harvest] isHarvestingGetDropsOld " + blockId); //added for rustic, it uses this version, other one does not work //https://github.com/the-realest-stu/Rustic/blob/c9bbdece4a97b159c63c7e3ba9bbf084aa7245bb/src/main/java/rustic/common/blocks/crops/BlockStakeCrop.java#L119 drops.addAll(blockCheck.getDrops(world, posCurrent, blockState, FORTUNE)); } else { blockCheck.getDrops(drops, world, posCurrent, blockState.withProperty(propInt, maxAge), FORTUNE); } world.setBlockState(posCurrent, blockState.withProperty(propInt, minAge)); if (isBreakAboveIfMatchingAfterHarvest(blockId)) { //for example: a 3 high block like corn if (doesBlockMatch(world, blockCheck, posCurrent.up())) { //ModCyclic.logger.log("[harvest] up(1) break " + blockId); //TODO: corn still drops a few from multiblock on ground. not the worst. world.destroyBlock(posCurrent.up(), false); } if (doesBlockMatch(world, blockCheck, posCurrent.up(2))) { // ModCyclic.logger.log("[harvest] up(2) break " + blockId); //TODO: corn still drops a few from multiblock on ground. not the worst. world.destroyBlock(posCurrent.up(2), false); } if (world.isAirBlock(posCurrent.down()) && world.isAirBlock(posCurrent.down(2))) { world.destroyBlock(posCurrent, false); } } // we have a blackist of crops to skip the remove-seed step boolean removeSeed = tryRemoveOneSeed && drops.size() > 1 && !doNotRemoveSeeds(blockId); if (removeSeed) { Item seedItem = blockCheck.getItemDropped(blockCheck.getDefaultState(), world.rand, 0); if (seedItem == null) { seedItem = Item.getItemFromBlock(blockCheck); } try { if (seedItem != null) { // if it dropped more than one ( seed and a thing) for (Iterator<ItemStack> iterator = drops.iterator(); iterator.hasNext();) { final ItemStack drop = iterator.next(); if (drop.getItem() == seedItem) { // Remove exactly one seed (consume for replanting // ModCyclic.logger.log("Harvester remove seed item " + seedItem); iterator.remove(); // ModCyclic.logger.log("yay remove seed " + drop.getDisplayName()); break; } } } } catch (Exception e) { ModCyclic.logger.error( "Crop could not be harvested by Cyclic, contact both mod authors " + blockId, e); } } //else dont remove seed } return drops; } }