diff --git a/src/main/java/de/ellpeck/naturesaura/Helper.java b/src/main/java/de/ellpeck/naturesaura/Helper.java index f6486ad0..fee749c5 100644 --- a/src/main/java/de/ellpeck/naturesaura/Helper.java +++ b/src/main/java/de/ellpeck/naturesaura/Helper.java @@ -241,4 +241,12 @@ public final class Helper { if (adv != null) playerMp.getAdvancements().grantCriterion(adv, criterion); } + + public static int getIngredientAmount(Ingredient ingredient) { + int highestAmount = 0; + for (ItemStack stack : ingredient.getMatchingStacks()) + if (stack.getCount() > highestAmount) + highestAmount = stack.getCount(); + return highestAmount; + } } diff --git a/src/main/java/de/ellpeck/naturesaura/api/NaturesAuraAPI.java b/src/main/java/de/ellpeck/naturesaura/api/NaturesAuraAPI.java index 9bc47674..6da16162 100644 --- a/src/main/java/de/ellpeck/naturesaura/api/NaturesAuraAPI.java +++ b/src/main/java/de/ellpeck/naturesaura/api/NaturesAuraAPI.java @@ -10,6 +10,7 @@ import de.ellpeck.naturesaura.api.internal.StubHooks; import de.ellpeck.naturesaura.api.multiblock.IMultiblock; import de.ellpeck.naturesaura.api.multiblock.Matcher; import de.ellpeck.naturesaura.api.recipes.AltarRecipe; +import de.ellpeck.naturesaura.api.recipes.AnimalSpawnerRecipe; import de.ellpeck.naturesaura.api.recipes.OfferingRecipe; import de.ellpeck.naturesaura.api.recipes.TreeRitualRecipe; import net.minecraft.block.BlockFlower; @@ -104,6 +105,12 @@ public final class NaturesAuraAPI { * using the multiblock maker debug tool. */ public static final Map MULTIBLOCKS = new HashMap<>(); + /** + * A map of all {@link AnimalSpawnerRecipe} objects that are used with the + * animal spawner block. To register a recipe, use {@link + * AnimalSpawnerRecipe#register()}. + */ + public static final Map ANIMAL_SPAWNER_RECIPES = new HashMap<>(); /** * The capability for any item or block that stores Aura in the form of an diff --git a/src/main/java/de/ellpeck/naturesaura/api/recipes/AnimalSpawnerRecipe.java b/src/main/java/de/ellpeck/naturesaura/api/recipes/AnimalSpawnerRecipe.java new file mode 100644 index 00000000..4c26c188 --- /dev/null +++ b/src/main/java/de/ellpeck/naturesaura/api/recipes/AnimalSpawnerRecipe.java @@ -0,0 +1,31 @@ +package de.ellpeck.naturesaura.api.recipes; + +import de.ellpeck.naturesaura.api.NaturesAuraAPI; +import net.minecraft.entity.Entity; +import net.minecraft.item.crafting.Ingredient; +import net.minecraft.util.ResourceLocation; +import net.minecraft.world.World; + +import java.util.function.Function; + +public class AnimalSpawnerRecipe { + + public final ResourceLocation name; + public final Ingredient[] ingredients; + public final Function entity; + public final int aura; + public final int time; + + public AnimalSpawnerRecipe(ResourceLocation name, Function entity, int aura, int time, Ingredient... ingredients) { + this.name = name; + this.ingredients = ingredients; + this.entity = entity; + this.aura = aura; + this.time = time; + } + + public AnimalSpawnerRecipe register() { + NaturesAuraAPI.ANIMAL_SPAWNER_RECIPES.put(this.name, this); + return this; + } +} diff --git a/src/main/java/de/ellpeck/naturesaura/api/recipes/OfferingRecipe.java b/src/main/java/de/ellpeck/naturesaura/api/recipes/OfferingRecipe.java index 21961a6c..551c0e04 100644 --- a/src/main/java/de/ellpeck/naturesaura/api/recipes/OfferingRecipe.java +++ b/src/main/java/de/ellpeck/naturesaura/api/recipes/OfferingRecipe.java @@ -1,7 +1,6 @@ package de.ellpeck.naturesaura.api.recipes; import de.ellpeck.naturesaura.api.NaturesAuraAPI; -import de.ellpeck.naturesaura.api.recipes.ing.AmountIngredient; import net.minecraft.item.ItemStack; import net.minecraft.item.crafting.Ingredient; import net.minecraft.util.ResourceLocation; @@ -9,11 +8,11 @@ import net.minecraft.util.ResourceLocation; public class OfferingRecipe { public final ResourceLocation name; - public final AmountIngredient input; + public final Ingredient input; public final Ingredient startItem; public final ItemStack output; - public OfferingRecipe(ResourceLocation name, AmountIngredient input, Ingredient startItem, ItemStack output) { + public OfferingRecipe(ResourceLocation name, Ingredient input, Ingredient startItem, ItemStack output) { this.name = name; this.input = input; this.startItem = startItem; diff --git a/src/main/java/de/ellpeck/naturesaura/blocks/BlockAnimalSpawner.java b/src/main/java/de/ellpeck/naturesaura/blocks/BlockAnimalSpawner.java new file mode 100644 index 00000000..71fc4e86 --- /dev/null +++ b/src/main/java/de/ellpeck/naturesaura/blocks/BlockAnimalSpawner.java @@ -0,0 +1,13 @@ +package de.ellpeck.naturesaura.blocks; + +import de.ellpeck.naturesaura.blocks.tiles.TileEntityAnimalSpawner; +import net.minecraft.block.SoundType; +import net.minecraft.block.material.Material; + +public class BlockAnimalSpawner extends BlockContainerImpl { + public BlockAnimalSpawner() { + super(Material.ROCK, "animal_spawner", TileEntityAnimalSpawner.class, "animal_spawner"); + this.setHardness(2F); + this.setSoundType(SoundType.STONE); + } +} diff --git a/src/main/java/de/ellpeck/naturesaura/blocks/ModBlocks.java b/src/main/java/de/ellpeck/naturesaura/blocks/ModBlocks.java index 5bbd98c6..89c10523 100644 --- a/src/main/java/de/ellpeck/naturesaura/blocks/ModBlocks.java +++ b/src/main/java/de/ellpeck/naturesaura/blocks/ModBlocks.java @@ -40,4 +40,5 @@ public final class ModBlocks { public static final Block ANIMAL_GENERATOR = new BlockAnimalGenerator(); public static final Block END_FLOWER = new BlockEndFlower(); public static final Block GRATED_CHUTE = new BlockGratedChute(); + public static final Block ANIMAL_SPAWNER = new BlockAnimalSpawner(); } diff --git a/src/main/java/de/ellpeck/naturesaura/blocks/multi/Multiblocks.java b/src/main/java/de/ellpeck/naturesaura/blocks/multi/Multiblocks.java index 706f31f2..96b0a5ab 100644 --- a/src/main/java/de/ellpeck/naturesaura/blocks/multi/Multiblocks.java +++ b/src/main/java/de/ellpeck/naturesaura/blocks/multi/Multiblocks.java @@ -63,4 +63,14 @@ public final class Multiblocks { (world, start, offset, pos, state, c) -> NaturesAuraAPI.FLOWERS.contains(state)), '0', ModBlocks.OFFERING_TABLE, ' ', Matcher.wildcard()); + public static final IMultiblock ANIMAL_SPAWNER = NaturesAuraAPI.instance().createMultiblock( + new ResourceLocation(NaturesAura.MOD_ID, "animal_spawner"), + new String[][]{ + {" ", " ", " ", " 0 ", " ", " ", " "}, + {" HHH ", " HRRRH ", "HRWRWRH", "HRR RRH", "HRWRWRH", " HRRRH ", " HHH "}}, + 'H', Blocks.HAY_BLOCK, + 'R', ModBlocks.INFUSED_BRICK, + 'W', ModBlocks.ANCIENT_PLANKS, + '0', ModBlocks.ANIMAL_SPAWNER, + ' ', Matcher.wildcard()); } diff --git a/src/main/java/de/ellpeck/naturesaura/blocks/tiles/TileEntityAnimalSpawner.java b/src/main/java/de/ellpeck/naturesaura/blocks/tiles/TileEntityAnimalSpawner.java new file mode 100644 index 00000000..2a2c6f5a --- /dev/null +++ b/src/main/java/de/ellpeck/naturesaura/blocks/tiles/TileEntityAnimalSpawner.java @@ -0,0 +1,162 @@ +package de.ellpeck.naturesaura.blocks.tiles; + +import de.ellpeck.naturesaura.Helper; +import de.ellpeck.naturesaura.api.NaturesAuraAPI; +import de.ellpeck.naturesaura.api.aura.chunk.IAuraChunk; +import de.ellpeck.naturesaura.api.aura.type.IAuraType; +import de.ellpeck.naturesaura.api.recipes.AnimalSpawnerRecipe; +import de.ellpeck.naturesaura.blocks.multi.Multiblocks; +import de.ellpeck.naturesaura.packet.PacketHandler; +import de.ellpeck.naturesaura.packet.PacketParticles; +import net.minecraft.entity.Entity; +import net.minecraft.entity.item.EntityItem; +import net.minecraft.item.ItemStack; +import net.minecraft.item.crafting.Ingredient; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.util.ITickable; +import net.minecraft.util.ResourceLocation; +import net.minecraft.util.math.AxisAlignedBB; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.MathHelper; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class TileEntityAnimalSpawner extends TileEntityImpl implements ITickable { + + private AnimalSpawnerRecipe currentRecipe; + private double spawnX; + private double spawnZ; + private int time; + private Entity entityClient; + + @Override + public void update() { + if (!this.world.isRemote) { + if (this.world.getTotalWorldTime() % 10 != 0) + return; + if (!Multiblocks.ANIMAL_SPAWNER.isComplete(this.world, this.pos)) { + if (this.currentRecipe != null) { + this.currentRecipe = null; + this.time = 0; + this.sendToClients(); + } + return; + } + + if (this.currentRecipe != null) { + int drain = MathHelper.ceil(this.currentRecipe.aura / (float) this.currentRecipe.time * 10F); + BlockPos spot = IAuraChunk.getHighestSpot(this.world, this.pos, 35, this.pos); + IAuraChunk.getAuraChunk(this.world, spot).drainAura(spot, drain); + + this.time += 10; + if (this.time >= this.currentRecipe.time) { + Entity entity = this.currentRecipe.entity.apply(this.world); + entity.setPosition(this.spawnX, this.pos.getY() + 1, this.spawnZ); + this.world.spawnEntity(entity); + + this.currentRecipe = null; + this.time = 0; + this.sendToClients(); + } + } else { + List items = this.world.getEntitiesWithinAABB(EntityItem.class, + new AxisAlignedBB(this.pos).grow(2)); + + for (AnimalSpawnerRecipe recipe : NaturesAuraAPI.ANIMAL_SPAWNER_RECIPES.values()) { + if (recipe.ingredients.length != items.size()) + continue; + List required = new ArrayList<>(Arrays.asList(recipe.ingredients)); + for (EntityItem item : items) { + if (item.isDead || item.cannotPickup()) + break; + ItemStack stack = item.getItem(); + if (stack.isEmpty()) + break; + for (Ingredient ingredient : required) { + if (ingredient.apply(stack) && Helper.getIngredientAmount(ingredient) == stack.getCount()) { + required.remove(ingredient); + break; + } + } + } + if (!required.isEmpty()) + continue; + + for (EntityItem item : items) { + item.setDead(); + PacketHandler.sendToAllAround(this.world, this.pos, 32, + new PacketParticles((float) item.posX, (float) item.posY, (float) item.posZ, 19)); + } + + this.currentRecipe = recipe; + this.spawnX = this.pos.getX() + 0.5 + this.world.rand.nextFloat() * 4 - 2; + this.spawnZ = this.pos.getZ() + 0.5 + this.world.rand.nextFloat() * 4 - 2; + this.sendToClients(); + break; + } + } + } else { + if (this.world.getTotalWorldTime() % 5 != 0) + return; + if (this.currentRecipe == null) { + this.entityClient = null; + return; + } + + NaturesAuraAPI.instance().spawnParticleStream( + this.pos.getX() + (float) this.world.rand.nextGaussian() * 5F, + this.pos.getY() + 1 + this.world.rand.nextFloat() * 5F, + this.pos.getZ() + (float) this.world.rand.nextGaussian() * 5F, + this.pos.getX() + this.world.rand.nextFloat(), + this.pos.getY() + this.world.rand.nextFloat(), + this.pos.getZ() + this.world.rand.nextFloat(), + this.world.rand.nextFloat() * 0.07F + 0.07F, + IAuraType.forWorld(this.world).getColor(), + this.world.rand.nextFloat() + 0.5F); + + if (this.entityClient == null) { + this.entityClient = this.currentRecipe.entity.apply(this.world); + this.entityClient.setPosition(this.spawnX, this.pos.getY() + 1, this.spawnZ); + } + AxisAlignedBB bounds = this.entityClient.getEntityBoundingBox(); + for (int i = this.world.rand.nextInt(5) + 5; i >= 0; i--) + NaturesAuraAPI.instance().spawnMagicParticle( + bounds.minX + this.world.rand.nextFloat() * (bounds.maxX - bounds.minX), + bounds.minY + this.world.rand.nextFloat() * (bounds.maxY - bounds.minY), + bounds.minZ + this.world.rand.nextFloat() * (bounds.maxZ - bounds.minZ), + 0F, 0F, 0F, 0x2fd8d3, 2F, 60, 0F, false, true); + } + } + + @Override + public void writeNBT(NBTTagCompound compound, SaveType type) { + super.writeNBT(compound, type); + if (type != SaveType.BLOCK) { + if (this.currentRecipe != null) { + compound.setString("recipe", this.currentRecipe.name.toString()); + compound.setDouble("spawn_x", this.spawnX); + compound.setDouble("spawn_z", this.spawnZ); + compound.setInteger("time", this.time); + } + } + } + + @Override + public void readNBT(NBTTagCompound compound, SaveType type) { + super.readNBT(compound, type); + if (type != SaveType.BLOCK) { + if (compound.hasKey("recipe")) { + ResourceLocation name = new ResourceLocation(compound.getString("recipe")); + this.currentRecipe = NaturesAuraAPI.ANIMAL_SPAWNER_RECIPES.get(name); + this.spawnX = compound.getDouble("spawn_x"); + this.spawnZ = compound.getDouble("spawn_z"); + this.time = compound.getInteger("time"); + } else { + this.currentRecipe = null; + this.time = 0; + } + } + } +} diff --git a/src/main/java/de/ellpeck/naturesaura/blocks/tiles/TileEntityOfferingTable.java b/src/main/java/de/ellpeck/naturesaura/blocks/tiles/TileEntityOfferingTable.java index 57782782..2034055d 100644 --- a/src/main/java/de/ellpeck/naturesaura/blocks/tiles/TileEntityOfferingTable.java +++ b/src/main/java/de/ellpeck/naturesaura/blocks/tiles/TileEntityOfferingTable.java @@ -1,5 +1,6 @@ package de.ellpeck.naturesaura.blocks.tiles; +import de.ellpeck.naturesaura.Helper; import de.ellpeck.naturesaura.api.NaturesAuraAPI; import de.ellpeck.naturesaura.api.recipes.OfferingRecipe; import de.ellpeck.naturesaura.blocks.multi.Multiblocks; @@ -60,8 +61,9 @@ public class TileEntityOfferingTable extends TileEntityImpl implements ITickable if (!recipe.startItem.apply(itemStack)) continue; - int recipeCount = stack.getCount() / recipe.input.amount; - stack.shrink(recipeCount * recipe.input.amount); + int amount = Helper.getIngredientAmount(recipe.input); + int recipeCount = stack.getCount() / amount; + stack.shrink(recipeCount * amount); item.setDead(); this.sendToClients(); diff --git a/src/main/java/de/ellpeck/naturesaura/items/ModItems.java b/src/main/java/de/ellpeck/naturesaura/items/ModItems.java index d962b613..ed1f8353 100644 --- a/src/main/java/de/ellpeck/naturesaura/items/ModItems.java +++ b/src/main/java/de/ellpeck/naturesaura/items/ModItems.java @@ -42,4 +42,5 @@ public final class ModItems { public static final Item SKY_INGOT = new ItemImpl("sky_ingot"); public static final Item CALLING_SPIRIT = new ItemGlowing("calling_spirit"); public static final Item EFFECT_POWDER = new ItemEffectPowder(); + public static final Item BIRTH_SPIRIT = new ItemImpl("birth_spirit"); } diff --git a/src/main/java/de/ellpeck/naturesaura/packet/PacketParticles.java b/src/main/java/de/ellpeck/naturesaura/packet/PacketParticles.java index a4f6a71c..3ee4cb15 100644 --- a/src/main/java/de/ellpeck/naturesaura/packet/PacketParticles.java +++ b/src/main/java/de/ellpeck/naturesaura/packet/PacketParticles.java @@ -312,6 +312,15 @@ public class PacketParticles implements IMessage { world.rand.nextGaussian() * 0.01F, color, 1.5F, 80, 0F, true, true); break; + case 19: // Animal spawner + for (int i = world.rand.nextInt(20) + 10; i >= 0; i--) + NaturesAuraAPI.instance().spawnMagicParticle( + message.posX, message.posY + 0.5F, message.posZ, + world.rand.nextGaussian() * 0.02F, + world.rand.nextFloat() * 0.02F, + world.rand.nextGaussian() * 0.02F, + 0x16b7b2, 1.5F, 40, 0F, false, true); + break; } } }); diff --git a/src/main/java/de/ellpeck/naturesaura/recipes/ModRecipes.java b/src/main/java/de/ellpeck/naturesaura/recipes/ModRecipes.java index 269653e2..0935b4d1 100644 --- a/src/main/java/de/ellpeck/naturesaura/recipes/ModRecipes.java +++ b/src/main/java/de/ellpeck/naturesaura/recipes/ModRecipes.java @@ -4,6 +4,7 @@ import de.ellpeck.naturesaura.Helper; import de.ellpeck.naturesaura.NaturesAura; import de.ellpeck.naturesaura.api.NaturesAuraAPI; import de.ellpeck.naturesaura.api.recipes.AltarRecipe; +import de.ellpeck.naturesaura.api.recipes.AnimalSpawnerRecipe; import de.ellpeck.naturesaura.api.recipes.OfferingRecipe; import de.ellpeck.naturesaura.api.recipes.TreeRitualRecipe; import de.ellpeck.naturesaura.api.recipes.ing.AmountIngredient; @@ -18,8 +19,11 @@ import de.ellpeck.naturesaura.items.ModItems; import net.minecraft.block.Block; import net.minecraft.block.BlockFlower; import net.minecraft.block.BlockStoneBrick; +import net.minecraft.entity.passive.EntityCow; +import net.minecraft.entity.passive.EntitySheep; import net.minecraft.init.Blocks; import net.minecraft.init.Items; +import net.minecraft.item.EnumDyeColor; import net.minecraft.item.ItemStack; import net.minecraft.item.crafting.Ingredient; import net.minecraft.util.ResourceLocation; @@ -131,6 +135,22 @@ public final class ModRecipes { Ingredient.fromItem(ModItems.CALLING_SPIRIT), new ItemStack(ModItems.SKY_INGOT)).register(); + new AnimalSpawnerRecipe(new ResourceLocation(NaturesAura.MOD_ID, "cow"), + EntityCow::new, 500, 60, + Ingredient.fromItem(ModItems.BIRTH_SPIRIT), + Ingredient.fromItem(Items.BEEF), + Ingredient.fromItem(Items.LEATHER)).register(); + for (EnumDyeColor color : EnumDyeColor.values()) + new AnimalSpawnerRecipe(new ResourceLocation(NaturesAura.MOD_ID, "sheep_" + color.getName()), + world -> { + EntitySheep sheep = new EntitySheep(world); + sheep.setFleeceColor(color); + return sheep; + }, 500, 60, + Ingredient.fromItem(ModItems.BIRTH_SPIRIT), + Ingredient.fromItem(Items.MUTTON), + Ingredient.fromStacks(new ItemStack(Blocks.WOOL, 1, color.getMetadata()))).register(); + NaturesAuraAPI.BOTANIST_PICKAXE_CONVERSIONS.put( Blocks.COBBLESTONE.getDefaultState(), Blocks.MOSSY_COBBLESTONE.getDefaultState());