diff --git a/src/main/java/de/ellpeck/naturesaura/Helper.java b/src/main/java/de/ellpeck/naturesaura/Helper.java index 98ffe255..9ef7393d 100644 --- a/src/main/java/de/ellpeck/naturesaura/Helper.java +++ b/src/main/java/de/ellpeck/naturesaura/Helper.java @@ -46,16 +46,6 @@ public final class Helper { } } - public static boolean checkMultiblock(World world, BlockPos pos, BlockPos[] positions, IBlockState state, boolean blockOnly) { - for (BlockPos offset : positions) { - IBlockState current = world.getBlockState(pos.add(offset)); - if (blockOnly ? current.getBlock() != state.getBlock() : current != state) { - return false; - } - } - return true; - } - @SideOnly(Side.CLIENT) public static int blendColors(int c1, int c2, float ratio) { int a = (int) ((c1 >> 24 & 0xFF) * ratio + (c2 >> 24 & 0xFF) * (1 - ratio)); diff --git a/src/main/java/de/ellpeck/naturesaura/NaturesAura.java b/src/main/java/de/ellpeck/naturesaura/NaturesAura.java index bfad6fbe..9f025bdb 100644 --- a/src/main/java/de/ellpeck/naturesaura/NaturesAura.java +++ b/src/main/java/de/ellpeck/naturesaura/NaturesAura.java @@ -7,6 +7,7 @@ import de.ellpeck.naturesaura.aura.chunk.AuraChunk; import de.ellpeck.naturesaura.aura.container.IAuraContainer; import de.ellpeck.naturesaura.aura.item.IAuraRecharge; import de.ellpeck.naturesaura.blocks.ModBlocks; +import de.ellpeck.naturesaura.blocks.Multiblocks; import de.ellpeck.naturesaura.commands.CommandAura; import de.ellpeck.naturesaura.compat.Compat; import de.ellpeck.naturesaura.events.CommonEvents; @@ -63,6 +64,7 @@ public final class NaturesAura { Compat.init(); PacketHandler.init(); ModRegistry.preInit(event); + new Multiblocks(); MinecraftForge.TERRAIN_GEN_BUS.register(new TerrainGenEvents()); MinecraftForge.EVENT_BUS.register(new CommonEvents()); diff --git a/src/main/java/de/ellpeck/naturesaura/blocks/BlockGoldPowder.java b/src/main/java/de/ellpeck/naturesaura/blocks/BlockGoldPowder.java index 3f50bca0..0c0ada31 100644 --- a/src/main/java/de/ellpeck/naturesaura/blocks/BlockGoldPowder.java +++ b/src/main/java/de/ellpeck/naturesaura/blocks/BlockGoldPowder.java @@ -1,8 +1,6 @@ package de.ellpeck.naturesaura.blocks; -import de.ellpeck.naturesaura.Helper; import de.ellpeck.naturesaura.NaturesAura; -import de.ellpeck.naturesaura.blocks.tiles.TileEntityWoodStand; import de.ellpeck.naturesaura.reg.IColorProvidingBlock; import net.minecraft.block.Block; import net.minecraft.block.SoundType; @@ -16,6 +14,7 @@ import net.minecraft.init.Blocks; import net.minecraft.util.BlockRenderLayer; import net.minecraft.util.EnumFacing; import net.minecraft.util.IStringSerializable; +import net.minecraft.util.Rotation; import net.minecraft.util.math.AxisAlignedBB; import net.minecraft.util.math.BlockPos; import net.minecraft.world.IBlockAccess; @@ -57,25 +56,6 @@ public class BlockGoldPowder extends BlockImpl implements IColorProvidingBlock { this.setHardness(0F); } - @Override - public void randomDisplayTick(IBlockState stateIn, World worldIn, BlockPos pos, Random rand) { - if (rand.nextFloat() >= 0.9F) { - for (BlockPos offsetToOrigin : TileEntityWoodStand.GOLD_POWDER_POSITIONS) { - BlockPos origin = pos.subtract(offsetToOrigin); - if (Helper.checkMultiblock(worldIn, origin, TileEntityWoodStand.GOLD_POWDER_POSITIONS, ModBlocks.GOLD_POWDER.getDefaultState(), true)) { - AxisAlignedBB box = this.getBoundingBox(stateIn, worldIn, pos); - NaturesAura.proxy.spawnMagicParticle(worldIn, - pos.getX() + box.minX + (box.maxX - box.minX) * rand.nextFloat(), - pos.getY() + 0.05F, - pos.getZ() + box.minZ + (box.maxZ - box.minZ) * rand.nextFloat(), - rand.nextGaussian() * 0.001, rand.nextFloat() * 0.001 + 0.005, rand.nextGaussian() * 0.001, - 0xf4cb42, 1F, 50, 0F, false, true); - break; - } - } - } - } - @Override protected BlockStateContainer createBlockState() { return new BlockStateContainer(this, NORTH, EAST, SOUTH, WEST); diff --git a/src/main/java/de/ellpeck/naturesaura/blocks/Multiblocks.java b/src/main/java/de/ellpeck/naturesaura/blocks/Multiblocks.java new file mode 100644 index 00000000..f4d85d04 --- /dev/null +++ b/src/main/java/de/ellpeck/naturesaura/blocks/Multiblocks.java @@ -0,0 +1,64 @@ +package de.ellpeck.naturesaura.blocks; + +import de.ellpeck.naturesaura.NaturesAura; +import net.minecraft.block.Block; +import net.minecraft.block.BlockLog; +import net.minecraft.block.BlockSapling; +import net.minecraft.block.BlockStoneBrick; +import net.minecraft.block.BlockStoneBrick.EnumType; +import net.minecraft.init.Blocks; +import net.minecraft.util.ResourceLocation; +import net.minecraft.util.Rotation; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; +import vazkii.patchouli.common.multiblock.Multiblock; +import vazkii.patchouli.common.multiblock.Multiblock.StateMatcher; +import vazkii.patchouli.common.multiblock.MultiblockRegistry; + +public final class Multiblocks { + + public static final Multiblock ALTAR = MultiblockRegistry.registerMultiblock( + new ResourceLocation(NaturesAura.MOD_ID, "altar"), + new Multiblock(new String[][]{ + {" M ", " ", " ", " ", "M M", " ", " ", " ", " M "}, + {" B ", " ", " ", " ", "B B", " ", " ", " ", " B "}, + {" B ", " ", " M M ", " ", "B 0 B", " ", " M M ", " ", " B "}, + {" ", " WBW ", " WBW ", " WWCWCWW ", " BBW WBB ", " WWCWCWW ", " WBW ", " WBW ", " "}}, + 'C', Blocks.STONEBRICK.getDefaultState().withProperty(BlockStoneBrick.VARIANT, EnumType.CHISELED), + 'B', Blocks.STONEBRICK.getDefaultState(), + 'W', Blocks.PLANKS, + 'M', Blocks.STONEBRICK.getDefaultState().withProperty(BlockStoneBrick.VARIANT, EnumType.MOSSY), + '0', ModBlocks.NATURE_ALTAR, + ' ', StateMatcher.ANY) + ).setSymmetrical(true); + public static final Multiblock TREE_RITUAL = MultiblockRegistry.registerMultiblock( + new ResourceLocation(NaturesAura.MOD_ID, "tree_ritual"), + new Multiblock(new String[][]{ + {" W ", " W W ", " GGG ", " GG GG ", "W G 0 G W", " GG GG ", " GGG ", " W W ", " W "}}, + 'W', ModBlocks.WOOD_STAND, + 'G', ModBlocks.GOLD_POWDER, + '0', StateMatcher.fromPredicate(Blocks.SAPLING, state -> state.getBlock() instanceof BlockSapling || state.getBlock() instanceof BlockLog), + ' ', StateMatcher.ANY) + ).setSymmetrical(true); + + public static boolean validateLoosely(Multiblock mb, World world, BlockPos pos, Block ignored) { + return validateLoosely(mb, pos, + (start, x, y, z, matcher) -> matcher.displayState.getBlock() == ignored || mb.test(world, start, x, y, z, Rotation.NONE)); + } + + public static boolean validateLoosely(Multiblock mb, BlockPos pos, LooseValidator validator) { + BlockPos start = pos.add(-mb.offX, -mb.offY, -mb.offZ); + for (int x = 0; x < mb.sizeX; x++) + for (int y = 0; y < mb.sizeY; y++) + for (int z = 0; z < mb.sizeZ; z++) { + if (!validator.works(start, x, y, z, mb.stateTargets[x][y][z])) { + return false; + } + } + return true; + } + + private interface LooseValidator { + boolean works(BlockPos start, int x, int y, int z, StateMatcher matcher); + } +} diff --git a/src/main/java/de/ellpeck/naturesaura/blocks/tiles/TileEntityNatureAltar.java b/src/main/java/de/ellpeck/naturesaura/blocks/tiles/TileEntityNatureAltar.java index 90fb550d..1caac693 100644 --- a/src/main/java/de/ellpeck/naturesaura/blocks/tiles/TileEntityNatureAltar.java +++ b/src/main/java/de/ellpeck/naturesaura/blocks/tiles/TileEntityNatureAltar.java @@ -6,14 +6,12 @@ import de.ellpeck.naturesaura.aura.Capabilities; import de.ellpeck.naturesaura.aura.chunk.AuraChunk; import de.ellpeck.naturesaura.aura.container.BasicAuraContainer; import de.ellpeck.naturesaura.aura.container.IAuraContainer; +import de.ellpeck.naturesaura.blocks.Multiblocks; import de.ellpeck.naturesaura.packet.PacketHandler; import de.ellpeck.naturesaura.packet.PacketParticleStream; import de.ellpeck.naturesaura.packet.PacketParticles; import de.ellpeck.naturesaura.recipes.AltarRecipe; -import net.minecraft.block.BlockStoneBrick; -import net.minecraft.block.BlockStoneBrick.EnumType; import net.minecraft.block.state.IBlockState; -import net.minecraft.init.Blocks; import net.minecraft.init.SoundEvents; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; @@ -29,71 +27,6 @@ import java.util.Random; public class TileEntityNatureAltar extends TileEntityImpl implements ITickable { - private static final BlockPos[] BRICK_POSITIONS = new BlockPos[]{ - new BlockPos(-2, -1, 0), - new BlockPos(-3, -1, 0), - new BlockPos(-4, 0, 0), - new BlockPos(-4, 1, 0), - - new BlockPos(2, -1, 0), - new BlockPos(3, -1, 0), - new BlockPos(4, 0, 0), - new BlockPos(4, 1, 0), - - new BlockPos(0, -1, -2), - new BlockPos(0, -1, -3), - new BlockPos(0, 0, -4), - new BlockPos(0, 1, -4), - - new BlockPos(0, -1, 2), - new BlockPos(0, -1, 3), - new BlockPos(0, 0, 4), - new BlockPos(0, 1, 4) - }; - private static final BlockPos[] MOSSY_POSITIONS = new BlockPos[]{ - new BlockPos(-4, 2, 0), - new BlockPos(4, 2, 0), - new BlockPos(0, 2, -4), - new BlockPos(0, 2, 4), - - new BlockPos(-2, 0, -2), - new BlockPos(2, 0, -2), - new BlockPos(2, 0, 2), - new BlockPos(-2, 0, 2) - }; - private static final BlockPos[] CHISELED_POSITIONS = new BlockPos[]{ - new BlockPos(1, -1, 1), - new BlockPos(-1, -1, 1), - new BlockPos(-1, -1, -1), - new BlockPos(1, -1, -1) - }; - private static final BlockPos[] WOOD_POSITIONS = new BlockPos[]{ - new BlockPos(-1, -1, 0), - new BlockPos(1, -1, 0), - new BlockPos(0, -1, -1), - new BlockPos(0, -1, 1), - - new BlockPos(-2, -1, -1), - new BlockPos(-3, -1, -1), - new BlockPos(-2, -1, 1), - new BlockPos(-3, -1, 1), - - new BlockPos(2, -1, -1), - new BlockPos(3, -1, -1), - new BlockPos(2, -1, 1), - new BlockPos(3, -1, 1), - - new BlockPos(-1, -1, -2), - new BlockPos(-1, -1, -3), - new BlockPos(1, -1, -2), - new BlockPos(1, -1, -3), - - new BlockPos(-1, -1, 2), - new BlockPos(-1, -1, 3), - new BlockPos(1, -1, 2), - new BlockPos(1, -1, 3), - }; - public final ItemStackHandler items = new ItemStackHandlerNA(1, this, true) { @Override public int getSlotLimit(int slot) { @@ -125,10 +58,7 @@ public class TileEntityNatureAltar extends TileEntityImpl implements ITickable { if (!this.world.isRemote) { if (this.world.getTotalWorldTime() % 40 == 0) { - boolean fine = this.check(BRICK_POSITIONS, Blocks.STONEBRICK.getDefaultState(), false) - && this.check(MOSSY_POSITIONS, Blocks.STONEBRICK.getDefaultState().withProperty(BlockStoneBrick.VARIANT, EnumType.MOSSY), false) - && this.check(CHISELED_POSITIONS, Blocks.STONEBRICK.getDefaultState().withProperty(BlockStoneBrick.VARIANT, EnumType.CHISELED), false) - && this.check(WOOD_POSITIONS, Blocks.PLANKS.getDefaultState(), true); + boolean fine = Multiblocks.ALTAR.validate(this.world, this.pos); if (fine != this.structureFine) { this.structureFine = fine; this.sendToClients(); @@ -238,10 +168,6 @@ public class TileEntityNatureAltar extends TileEntityImpl implements ITickable { } } - private boolean check(BlockPos[] positions, IBlockState state, boolean blockOnly) { - return Helper.checkMultiblock(this.world, this.pos, positions, state, blockOnly); - } - @Override public void writeNBT(NBTTagCompound compound, boolean syncing) { super.writeNBT(compound, syncing); diff --git a/src/main/java/de/ellpeck/naturesaura/blocks/tiles/TileEntityWoodStand.java b/src/main/java/de/ellpeck/naturesaura/blocks/tiles/TileEntityWoodStand.java index 4c5c3104..c7b2092c 100644 --- a/src/main/java/de/ellpeck/naturesaura/blocks/tiles/TileEntityWoodStand.java +++ b/src/main/java/de/ellpeck/naturesaura/blocks/tiles/TileEntityWoodStand.java @@ -2,50 +2,31 @@ package de.ellpeck.naturesaura.blocks.tiles; import de.ellpeck.naturesaura.Helper; import de.ellpeck.naturesaura.blocks.ModBlocks; +import de.ellpeck.naturesaura.blocks.Multiblocks; import de.ellpeck.naturesaura.packet.PacketHandler; import de.ellpeck.naturesaura.packet.PacketParticleStream; import de.ellpeck.naturesaura.packet.PacketParticles; +import de.ellpeck.naturesaura.recipes.TreeRitualRecipe; import net.minecraft.block.BlockLeaves; import net.minecraft.block.BlockLog; import net.minecraft.block.state.IBlockState; import net.minecraft.entity.item.EntityItem; import net.minecraft.init.SoundEvents; import net.minecraft.item.ItemStack; -import net.minecraft.nbt.NBTBase; import net.minecraft.nbt.NBTTagCompound; -import net.minecraft.nbt.NBTTagList; import net.minecraft.tileentity.TileEntity; -import net.minecraft.util.EnumFacing; -import net.minecraft.util.ITickable; -import net.minecraft.util.SoundCategory; +import net.minecraft.util.*; import net.minecraft.util.math.BlockPos; import net.minecraftforge.items.IItemHandlerModifiable; import net.minecraftforge.items.ItemStackHandler; +import org.apache.commons.lang3.mutable.MutableBoolean; -import java.util.HashMap; -import java.util.Map; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; public class TileEntityWoodStand extends TileEntityImpl implements ITickable { - public static final BlockPos[] GOLD_POWDER_POSITIONS = new BlockPos[]{ - new BlockPos(-2, 0, 0), - new BlockPos(2, 0, 0), - new BlockPos(0, 0, -2), - new BlockPos(0, 0, 2), - new BlockPos(-1, 0, -1), - new BlockPos(-1, 0, 1), - new BlockPos(1, 0, 1), - new BlockPos(1, 0, -1), - new BlockPos(2, 0, -1), - new BlockPos(2, 0, 1), - new BlockPos(-2, 0, -1), - new BlockPos(-2, 0, 1), - new BlockPos(1, 0, 2), - new BlockPos(-1, 0, 2), - new BlockPos(1, 0, -2), - new BlockPos(-1, 0, -2) - }; - public final ItemStackHandler items = new ItemStackHandlerNA(1, this, true) { @Override public int getSlotLimit(int slot) { @@ -53,86 +34,86 @@ public class TileEntityWoodStand extends TileEntityImpl implements ITickable { } }; + private TreeRitualRecipe recipe; private BlockPos ritualPos; - private Map involvedStands; - private ItemStack output; - private int totalTime; private int timer; - public void setRitual(BlockPos pos, ItemStack output, int totalTime, Map involvedStands) { + public void setRitual(BlockPos pos, TreeRitualRecipe recipe) { this.ritualPos = pos; - this.output = output; - this.totalTime = totalTime; - this.involvedStands = involvedStands; + this.recipe = recipe; } @Override public void update() { if (!this.world.isRemote) { - if (this.ritualPos != null && this.involvedStands != null && this.output != null && this.totalTime > 0) { - if (this.isRitualOkay()) { - this.timer++; + if (this.ritualPos != null && this.recipe != null) { + if (this.world.getTotalWorldTime() % 5 == 0) { + if (this.isRitualOkay()) { + boolean wasOverHalf = this.timer >= this.recipe.time / 2; + this.timer += 5; + boolean isOverHalf = this.timer >= this.recipe.time / 2; + + if (!isOverHalf) + Multiblocks.TREE_RITUAL.forEach(this.world, this.ritualPos, Rotation.NONE, 'W', pos -> { + TileEntity tile = this.world.getTileEntity(pos); + if (tile instanceof TileEntityWoodStand && !((TileEntityWoodStand) tile).items.getStackInSlot(0).isEmpty()) { + PacketHandler.sendToAllAround(this.world, this.pos, 32, new PacketParticleStream( + (float) pos.getX() + 0.2F + this.world.rand.nextFloat() * 0.6F, + (float) pos.getY() + 0.85F, + (float) pos.getZ() + 0.2F + this.world.rand.nextFloat() * 0.6F, + this.ritualPos.getX() + 0.5F, this.ritualPos.getY() + this.world.rand.nextFloat() * 3F + 2F, this.ritualPos.getZ() + 0.5F, + this.world.rand.nextFloat() * 0.02F + 0.02F, 0x89cc37, this.world.rand.nextFloat() * 1F + 1F + )); + } + }); - if (this.timer % 5 == 0 && this.timer < this.totalTime / 2) { - for (BlockPos pos : this.involvedStands.keySet()) { - PacketHandler.sendToAllAround(this.world, this.pos, 32, new PacketParticleStream( - (float) pos.getX() + 0.2F + this.world.rand.nextFloat() * 0.6F, - (float) pos.getY() + 0.85F, - (float) pos.getZ() + 0.2F + this.world.rand.nextFloat() * 0.6F, - this.ritualPos.getX() + 0.5F, this.ritualPos.getY() + this.world.rand.nextFloat() * 3F + 2F, this.ritualPos.getZ() + 0.5F, - this.world.rand.nextFloat() * 0.02F + 0.02F, 0x89cc37, this.world.rand.nextFloat() * 1F + 1F - )); - } - } - if (this.timer % 5 == 0) { PacketHandler.sendToAllAround(this.world, this.ritualPos, 32, new PacketParticles(this.ritualPos.getX(), this.ritualPos.getY(), this.ritualPos.getZ(), 0)); - } - if (this.timer >= this.totalTime) { - this.recurseTreeDestruction(this.ritualPos, this.ritualPos); - for (BlockPos offset : GOLD_POWDER_POSITIONS) { - this.world.setBlockToAir(this.ritualPos.add(offset)); + if (this.timer >= this.recipe.time) { + this.recurseTreeDestruction(this.ritualPos, this.ritualPos); + Multiblocks.TREE_RITUAL.forEach(this.world, this.ritualPos, Rotation.NONE, 'G', + pos -> this.world.setBlockToAir(pos)); + + EntityItem item = new EntityItem(this.world, + this.ritualPos.getX() + 0.5, this.ritualPos.getY() + 4.5, this.ritualPos.getZ() + 0.5, + this.recipe.result.copy()); + this.world.spawnEntity(item); + + PacketHandler.sendToAllAround(this.world, this.pos, 32, + new PacketParticles((float) item.posX, (float) item.posY, (float) item.posZ, 3)); + this.world.playSound(null, this.pos.getX() + 0.5, this.pos.getY() + 0.5, this.pos.getZ() + 0.5, + SoundEvents.ENTITY_ENDERMEN_TELEPORT, SoundCategory.BLOCKS, 0.65F, 1F); + + this.ritualPos = null; + this.recipe = null; + this.timer = 0; + } else if (isOverHalf && !wasOverHalf) { + Multiblocks.TREE_RITUAL.forEach(this.world, this.ritualPos, Rotation.NONE, 'W', pos -> { + TileEntity tile = this.world.getTileEntity(pos); + if (tile instanceof TileEntityWoodStand) { + TileEntityWoodStand stand = (TileEntityWoodStand) tile; + if (!stand.items.getStackInSlot(0).isEmpty()) { + PacketHandler.sendToAllAround(this.world, this.pos, 32, + new PacketParticles(stand.pos.getX(), stand.pos.getY(), stand.pos.getZ(), 1)); + this.world.playSound(null, stand.pos.getX() + 0.5, stand.pos.getY() + 0.5, stand.pos.getZ() + 0.5, + SoundEvents.BLOCK_WOOD_STEP, SoundCategory.BLOCKS, 0.5F, 1F); + + stand.items.setStackInSlot(0, ItemStack.EMPTY); + stand.sendToClients(); + } + } + }); } - - EntityItem item = new EntityItem(this.world, - this.ritualPos.getX() + 0.5, this.ritualPos.getY() + 4.5, this.ritualPos.getZ() + 0.5, - this.output.copy()); - this.world.spawnEntity(item); - - PacketHandler.sendToAllAround(this.world, this.pos, 32, - new PacketParticles((float) item.posX, (float) item.posY, (float) item.posZ, 3)); - this.world.playSound(null, this.pos.getX() + 0.5, this.pos.getY() + 0.5, this.pos.getZ() + 0.5, - SoundEvents.ENTITY_ENDERMEN_TELEPORT, SoundCategory.BLOCKS, 0.65F, 1F); - + } else { this.ritualPos = null; - this.involvedStands = null; - this.output = null; - this.totalTime = 0; + this.recipe = null; this.timer = 0; - } else if (this.timer == this.totalTime / 2) { - for (BlockPos pos : this.involvedStands.keySet()) { - TileEntityWoodStand stand = (TileEntityWoodStand) this.world.getTileEntity(pos); - - PacketHandler.sendToAllAround(this.world, this.pos, 32, new PacketParticles(stand.pos.getX(), stand.pos.getY(), stand.pos.getZ(), 1)); - this.world.playSound(null, stand.pos.getX() + 0.5, stand.pos.getY() + 0.5, stand.pos.getZ() + 0.5, - SoundEvents.BLOCK_WOOD_STEP, SoundCategory.BLOCKS, 0.5F, 1F); - - stand.items.setStackInSlot(0, ItemStack.EMPTY); - stand.sendToClients(); - } - } - - } else { - this.ritualPos = null; - this.involvedStands = null; - this.output = null; - this.totalTime = 0; - this.timer = 0; } } } + } private void recurseTreeDestruction(BlockPos pos, BlockPos start) { @@ -155,20 +136,29 @@ public class TileEntityWoodStand extends TileEntityImpl implements ITickable { } private boolean isRitualOkay() { - for (int i = 0; i < 3; i++) { - IBlockState state = this.world.getBlockState(this.ritualPos.up(i)); - if (!(state.getBlock() instanceof BlockLog)) { - return false; - } + if (!Multiblocks.validateLoosely(Multiblocks.TREE_RITUAL, this.world, this.ritualPos, ModBlocks.WOOD_STAND)) { + return false; } - for (Map.Entry entry : this.involvedStands.entrySet()) { - TileEntity tile = this.world.getTileEntity(entry.getKey()); - if (!(tile instanceof TileEntityWoodStand) - || (this.timer < this.totalTime / 2 && !((TileEntityWoodStand) tile).items.getStackInSlot(0).isItemEqual(entry.getValue()))) { - return false; - } - } - return Helper.checkMultiblock(this.world, this.ritualPos, TileEntityWoodStand.GOLD_POWDER_POSITIONS, ModBlocks.GOLD_POWDER.getDefaultState(), true); + if (this.timer < this.recipe.time / 2) { + List required = new ArrayList<>(Arrays.asList(this.recipe.items)); + MutableBoolean tooMuch = new MutableBoolean(); + Multiblocks.TREE_RITUAL.forEach(this.world, this.ritualPos, Rotation.NONE, 'W', pos -> { + TileEntity tile = this.world.getTileEntity(pos); + if (tile instanceof TileEntityWoodStand) { + ItemStack stack = ((TileEntityWoodStand) tile).items.getStackInSlot(0); + if (!stack.isEmpty()) { + int index = Helper.getItemIndex(required, stack); + if (index >= 0) { + required.remove(index); + } else { + tooMuch.setTrue(); + } + } + } + }); + return tooMuch.isFalse() && required.isEmpty(); + } else + return true; } @Override @@ -177,20 +167,10 @@ public class TileEntityWoodStand extends TileEntityImpl implements ITickable { compound.setTag("items", this.items.serializeNBT()); if (!syncing) { - if (this.ritualPos != null && this.involvedStands != null && this.output != null && this.totalTime > 0) { + if (this.ritualPos != null && this.recipe != null) { compound.setLong("ritual_pos", this.ritualPos.toLong()); compound.setInteger("timer", this.timer); - compound.setInteger("total_time", this.totalTime); - compound.setTag("output", this.output.writeToNBT(new NBTTagCompound())); - - NBTTagList list = new NBTTagList(); - for (Map.Entry entry : this.involvedStands.entrySet()) { - NBTTagCompound tag = new NBTTagCompound(); - tag.setLong("pos", entry.getKey().toLong()); - tag.setTag("item", entry.getValue().writeToNBT(new NBTTagCompound())); - list.appendTag(tag); - } - compound.setTag("stands", list); + compound.setString("recipe", this.recipe.name.toString()); } } } @@ -201,21 +181,10 @@ public class TileEntityWoodStand extends TileEntityImpl implements ITickable { this.items.deserializeNBT(compound.getCompoundTag("items")); if (!syncing) { - if (compound.hasKey("ritual_pos") && compound.hasKey("stands") && compound.hasKey("output") && compound.hasKey("total_time")) { + if (compound.hasKey("recipe")) { this.ritualPos = BlockPos.fromLong(compound.getLong("ritual_pos")); this.timer = compound.getInteger("timer"); - this.totalTime = compound.getInteger("total_time"); - this.output = new ItemStack(compound.getCompoundTag("output")); - - this.involvedStands = new HashMap<>(); - NBTTagList list = compound.getTagList("stands", 10); - for (NBTBase base : list) { - NBTTagCompound tag = (NBTTagCompound) base; - this.involvedStands.put( - BlockPos.fromLong(tag.getLong("pos")), - new ItemStack(tag.getCompoundTag("item")) - ); - } + this.recipe = TreeRitualRecipe.RECIPES.get(new ResourceLocation(compound.getString("recipe"))); } } } diff --git a/src/main/java/de/ellpeck/naturesaura/events/TerrainGenEvents.java b/src/main/java/de/ellpeck/naturesaura/events/TerrainGenEvents.java index 3265daa6..1991ff9c 100644 --- a/src/main/java/de/ellpeck/naturesaura/events/TerrainGenEvents.java +++ b/src/main/java/de/ellpeck/naturesaura/events/TerrainGenEvents.java @@ -2,16 +2,23 @@ package de.ellpeck.naturesaura.events; import de.ellpeck.naturesaura.Helper; import de.ellpeck.naturesaura.blocks.ModBlocks; +import de.ellpeck.naturesaura.blocks.Multiblocks; import de.ellpeck.naturesaura.blocks.tiles.TileEntityWoodStand; import de.ellpeck.naturesaura.recipes.TreeRitualRecipe; import net.minecraft.block.state.IBlockState; import net.minecraft.item.ItemStack; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.Rotation; import net.minecraft.util.math.BlockPos; import net.minecraft.world.World; import net.minecraftforge.event.terraingen.SaplingGrowTreeEvent; import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; +import org.apache.commons.lang3.mutable.MutableBoolean; +import org.apache.commons.lang3.mutable.MutableObject; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; public class TerrainGenEvents { @@ -20,44 +27,38 @@ public class TerrainGenEvents { World world = event.getWorld(); BlockPos pos = event.getPos(); if (!world.isRemote) { - if (Helper.checkMultiblock(world, pos, TileEntityWoodStand.GOLD_POWDER_POSITIONS, ModBlocks.GOLD_POWDER.getDefaultState(), true)) { - List stands = new ArrayList<>(); - List usableItems = new ArrayList<>(); - Helper.getTileEntitiesInArea(world, pos, 5, tile -> { - if (tile instanceof TileEntityWoodStand) { - TileEntityWoodStand stand = (TileEntityWoodStand) tile; - ItemStack stack = stand.items.getStackInSlot(0); - if (!stack.isEmpty()) { - usableItems.add(stack); - stands.add(stand); - } - } - }); - + if (Multiblocks.validateLoosely(Multiblocks.TREE_RITUAL, world, pos, ModBlocks.WOOD_STAND)) { IBlockState sapling = world.getBlockState(pos); ItemStack saplingStack = sapling.getBlock().getItem(world, pos, sapling); if (!saplingStack.isEmpty()) { for (TreeRitualRecipe recipe : TreeRitualRecipe.RECIPES.values()) { - if (recipe.matchesItems(saplingStack, usableItems)) { - Map actuallyInvolved = new HashMap<>(); - List stillRequired = new ArrayList<>(Arrays.asList(recipe.items)); - TileEntityWoodStand toPick = null; + if (recipe.saplingType.isItemEqual(saplingStack)) { + List required = new ArrayList<>(Arrays.asList(recipe.items)); + MutableBoolean tooMuch = new MutableBoolean(); + MutableObject toPick = new MutableObject<>(); - for (TileEntityWoodStand stand : stands) { - ItemStack stack = stand.items.getStackInSlot(0); - int index = Helper.getItemIndex(stillRequired, stack); - if (index >= 0) { - actuallyInvolved.put(stand.getPos(), stack); - stillRequired.remove(index); + Multiblocks.TREE_RITUAL.forEach(world, pos, Rotation.NONE, 'W', tilePos -> { + TileEntity tile = world.getTileEntity(tilePos); + if (tile instanceof TileEntityWoodStand) { + TileEntityWoodStand stand = (TileEntityWoodStand) tile; + ItemStack stack = stand.items.getStackInSlot(0); + if (!stack.isEmpty()) { + int index = Helper.getItemIndex(required, stack); + if (index >= 0) { + required.remove(index); - if (toPick == null) { - toPick = stand; + if (toPick.getValue() == null) { + toPick.setValue(stand); + } + } else { + tooMuch.setTrue(); + } } } - } + }); - if (stillRequired.isEmpty()) { - toPick.setRitual(pos, recipe.result, recipe.time, actuallyInvolved); + if (tooMuch.isFalse() && required.isEmpty()) { + toPick.getValue().setRitual(pos, recipe); break; } } diff --git a/src/main/java/de/ellpeck/naturesaura/packet/PacketParticles.java b/src/main/java/de/ellpeck/naturesaura/packet/PacketParticles.java index 2a672a10..a78b782b 100644 --- a/src/main/java/de/ellpeck/naturesaura/packet/PacketParticles.java +++ b/src/main/java/de/ellpeck/naturesaura/packet/PacketParticles.java @@ -1,10 +1,11 @@ package de.ellpeck.naturesaura.packet; import de.ellpeck.naturesaura.NaturesAura; -import de.ellpeck.naturesaura.blocks.tiles.TileEntityWoodStand; +import de.ellpeck.naturesaura.blocks.Multiblocks; import io.netty.buffer.ByteBuf; import net.minecraft.block.state.IBlockState; import net.minecraft.client.Minecraft; +import net.minecraft.util.Rotation; import net.minecraft.util.math.AxisAlignedBB; import net.minecraft.util.math.BlockPos; import net.minecraft.world.World; @@ -59,8 +60,7 @@ public class PacketParticles implements IMessage { switch (message.type) { case 0: // Tree ritual: Gold powder BlockPos pos = new BlockPos(message.posX, message.posY, message.posZ); - for (BlockPos offset : TileEntityWoodStand.GOLD_POWDER_POSITIONS) { - BlockPos dustPos = pos.add(offset); + Multiblocks.TREE_RITUAL.forEach(world, pos, Rotation.NONE, 'G', dustPos -> { IBlockState state = world.getBlockState(dustPos); AxisAlignedBB box = state.getBoundingBox(world, dustPos); NaturesAura.proxy.spawnMagicParticle(world, @@ -71,7 +71,7 @@ public class PacketParticles implements IMessage { world.rand.nextFloat() * 0.005F + 0.01F, (float) world.rand.nextGaussian() * 0.01F, 0xf4cb42, 2F, 100, 0F, false, true); - } + }); break; case 1: // Tree ritual: Consuming item for (int i = world.rand.nextInt(20) + 10; i >= 0; i--) { diff --git a/src/main/java/de/ellpeck/naturesaura/recipes/ModRecipes.java b/src/main/java/de/ellpeck/naturesaura/recipes/ModRecipes.java index 7b108d67..290c2dd3 100644 --- a/src/main/java/de/ellpeck/naturesaura/recipes/ModRecipes.java +++ b/src/main/java/de/ellpeck/naturesaura/recipes/ModRecipes.java @@ -23,7 +23,7 @@ public final class ModRecipes { new ItemStack(Blocks.STONE), new ItemStack(Blocks.STONE), new ItemStack(ModItems.GOLD_LEAF), - new ItemStack(Items.DIAMOND)).add(); + new ItemStack(Items.GOLD_INGOT)).add(); new TreeRitualRecipe(new ResourceLocation(NaturesAura.MOD_ID, "ancient_sapling"), new ItemStack(Blocks.SAPLING), new ItemStack(ModBlocks.ANCIENT_SAPLING), 200, new ItemStack(Blocks.SAPLING), diff --git a/src/main/java/de/ellpeck/naturesaura/recipes/TreeRitualRecipe.java b/src/main/java/de/ellpeck/naturesaura/recipes/TreeRitualRecipe.java index 575a74a7..62e3b936 100644 --- a/src/main/java/de/ellpeck/naturesaura/recipes/TreeRitualRecipe.java +++ b/src/main/java/de/ellpeck/naturesaura/recipes/TreeRitualRecipe.java @@ -1,11 +1,9 @@ package de.ellpeck.naturesaura.recipes; -import de.ellpeck.naturesaura.Helper; import net.minecraft.item.ItemStack; import net.minecraft.util.ResourceLocation; import java.util.HashMap; -import java.util.List; import java.util.Map; public class TreeRitualRecipe { @@ -26,19 +24,6 @@ public class TreeRitualRecipe { this.time = time; } - public boolean matchesItems(ItemStack sapling, List items) { - if (this.saplingType.isItemEqual(sapling)) { - for (ItemStack ingredient : this.items) { - if (Helper.getItemIndex(items, ingredient) < 0) { - return false; - } - } - return true; - } else { - return false; - } - } - public TreeRitualRecipe add() { RECIPES.put(this.name, this); return this; diff --git a/src/main/resources/assets/naturesaura/patchouli_books/book/en_us/entries/collecting/altar.json b/src/main/resources/assets/naturesaura/patchouli_books/book/en_us/entries/collecting/altar.json index 3e3d1099..1cdf6c07 100644 --- a/src/main/resources/assets/naturesaura/patchouli_books/book/en_us/entries/collecting/altar.json +++ b/src/main/resources/assets/naturesaura/patchouli_books/book/en_us/entries/collecting/altar.json @@ -9,6 +9,11 @@ "type": "text", "text": "A rudimentary, yet effective way of collecting $(aura) early on is the $(item)Natural Altar$(). After creating the setup shown on the following page, the altar will start slowly draining $(aura) in the vicinity. However, it is not strong enough to cause major damage, making it only drain until there is none left in the area.$(br)The collected $(aura) can then be used in several ways, $(l:using/altar)infusing items$() for instance." }, + { + "type": "multiblock", + "multiblock": "naturesaura:altar", + "text": "How to assemble the $(item)Natural Altar$()" + }, { "type": "naturesaura:tree_ritual", "text": "Creating the $(item)Natural Altar$() using the $(l:practices/tree_ritual)Ritual of the Forest$()", diff --git a/src/main/resources/assets/naturesaura/patchouli_books/book/en_us/entries/practices/tree_ritual.json b/src/main/resources/assets/naturesaura/patchouli_books/book/en_us/entries/practices/tree_ritual.json index 5d579911..b337e9fd 100644 --- a/src/main/resources/assets/naturesaura/patchouli_books/book/en_us/entries/practices/tree_ritual.json +++ b/src/main/resources/assets/naturesaura/patchouli_books/book/en_us/entries/practices/tree_ritual.json @@ -11,7 +11,12 @@ }, { "type": "text", - "text": "To construct this $(thing)ritual$(), one has to place $(item)Gold Powder$() in the configuration shown on the next page, with some $(item)Wooden Stands$() around that which will hold the ingredients in whichever configuration seems visually pleasing.$(br)Then, the correct $(item)Sapling$() must be placed in the middle, and, either through some kind of fertilizer, or through the inevitability of time, its growth will cause the ritual to start." + "text": "To construct this $(thing)ritual$(), one has to place $(item)Gold Powder$() in the configuration shown on the next page, with some $(item)Wooden Stands$() around that which will hold the ingredients.$(br)Then, the correct $(item)Sapling$() must be placed in the middle, and, either through some kind of fertilizer, or through the inevitability of time, its growth will cause the ritual to start." + }, + { + "type": "multiblock", + "multiblock": "naturesaura:tree_ritual", + "text": "Preparing the $(item)Ritual of the Forest$(). It is not required to have all $(item)Wood Stands$() present." }, { "type": "crafting",