package mymod;
import java.util.Optional;
import java.util.Random;
import javax.annotation.Nullable;
import net.minecraft.block.Block;
import net.minecraft.block.BlockRenderType;
import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks;
import net.minecraft.block.ContainerBlock;
import net.minecraft.block.IWaterLoggable;
import net.minecraft.enchantment.EnchantmentHelper;
import net.minecraft.entity.Entity;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.projectile.AbstractArrowEntity;
import net.minecraft.fluid.Fluids;
import net.minecraft.fluid.IFluidState;
import net.minecraft.inventory.InventoryHelper;
import net.minecraft.item.BlockItemUseContext;
import net.minecraft.item.ItemStack;
import net.minecraft.item.crafting.CampfireCookingRecipe;
import net.minecraft.particles.BasicParticleType;
import net.minecraft.particles.ParticleTypes;
import net.minecraft.pathfinding.PathType;
import net.minecraft.state.BooleanProperty;
import net.minecraft.state.DirectionProperty;
import net.minecraft.state.StateContainer;
import net.minecraft.state.properties.BlockStateProperties;
import net.minecraft.stats.Stats;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.BlockRenderLayer;
import net.minecraft.util.DamageSource;
import net.minecraft.util.Direction;
import net.minecraft.util.Hand;
import net.minecraft.util.Mirror;
import net.minecraft.util.Rotation;
import net.minecraft.util.SoundCategory;
import net.minecraft.util.SoundEvents;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.BlockRayTraceResult;
import net.minecraft.util.math.shapes.ISelectionContext;
import net.minecraft.util.math.shapes.VoxelShape;
import net.minecraft.world.IBlockReader;
import net.minecraft.world.IWorld;
import net.minecraft.world.World;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
public class Campfire extends ContainerBlock implements IWaterLoggable {
protected static final VoxelShape SHAPE = Block.makeCuboidShape(0.0D, 0.0D, 0.0D, 16.0D, 7.0D, 16.0D);
public static final BooleanProperty LIT = BlockStateProperties.LIT;
public static final BooleanProperty SIGNAL_FIRE = BlockStateProperties.SIGNAL_FIRE;
public static final BooleanProperty WATERLOGGED = BlockStateProperties.WATERLOGGED;
public static final DirectionProperty FACING = BlockStateProperties.HORIZONTAL_FACING;
public Campfire(Block.Properties p_i49989_1_) {
super(p_i49989_1_);
this.setDefaultState(this.stateContainer.getBaseState().with(LIT, Boolean.valueOf(true)).with(SIGNAL_FIRE, Boolean.valueOf(false)).with(WATERLOGGED, Boolean.valueOf(false)).with(FACING, Direction.NORTH));
}
public boolean onBlockActivated(BlockState state, World worldIn, BlockPos pos, PlayerEntity player, Hand handIn, BlockRayTraceResult hit) {
if (state.get(LIT)) {
TileEntity tileentity = worldIn.getTileEntity(pos);
if (tileentity instanceof CampfireEntity) {
CampfireEntity CampfireEntity = (CampfireEntity)tileentity;
ItemStack itemstack = player.getHeldItem(handIn);
Optional<CampfireCookingRecipe> optional = CampfireEntity.findMatchingRecipe(itemstack);
if (optional.isPresent()) {
if (!worldIn.isRemote && CampfireEntity.addItem(player.abilities.isCreativeMode ? itemstack.copy() : itemstack, optional.get().getCookTime())) {
player.addStat(Stats.INTERACT_WITH_CAMPFIRE);
}
return true;
}
}
}
return false;
}
public void onEntityCollision(BlockState state, World worldIn, BlockPos pos, Entity entityIn) {
if (!entityIn.isImmuneToFire() && state.get(LIT) && entityIn instanceof LivingEntity && !EnchantmentHelper.hasFrostWalker((LivingEntity)entityIn)) {
entityIn.attackEntityFrom(DamageSource.IN_FIRE, 1.0F);
}
super.onEntityCollision(state, worldIn, pos, entityIn);
}
public void onReplaced(BlockState state, World worldIn, BlockPos pos, BlockState newState, boolean isMoving) {
if (state.getBlock() != newState.getBlock()) {
TileEntity tileentity = worldIn.getTileEntity(pos);
if (tileentity instanceof CampfireEntity) {
InventoryHelper.dropItems(worldIn, pos, ((CampfireEntity)tileentity).getInventory());
}
super.onReplaced(state, worldIn, pos, newState, isMoving);
}
}
@Nullable
public BlockState getStateForPlacement(BlockItemUseContext context) {
IWorld iworld = context.getWorld();
BlockPos blockpos = context.getPos();
boolean flag = iworld.getFluidState(blockpos).getFluid() == Fluids.WATER;
return this.getDefaultState().with(WATERLOGGED, Boolean.valueOf(flag)).with(SIGNAL_FIRE, Boolean.valueOf(this.isHayBlock(iworld.getBlockState(blockpos.down())))).with(LIT, Boolean.valueOf(!flag)).with(FACING, context.getPlacementHorizontalFacing());
}
/**
* Update the provided state given the provided neighbor facing and neighbor state, returning a new state.
* For example, fences make their connections to the passed in state if possible, and wet concrete powder immediately
* returns its solidified counterpart.
* Note that this method should ideally consider only the specific face passed in.
*/
public BlockState updatePostPlacement(BlockState stateIn, Direction facing, BlockState facingState, IWorld worldIn, BlockPos currentPos, BlockPos facingPos) {
if (stateIn.get(WATERLOGGED)) {
worldIn.getPendingFluidTicks().scheduleTick(currentPos, Fluids.WATER, Fluids.WATER.getTickRate(worldIn));
}
return facing == Direction.DOWN ? stateIn.with(SIGNAL_FIRE, Boolean.valueOf(this.isHayBlock(facingState))) : super.updatePostPlacement(stateIn, facing, facingState, worldIn, currentPos, facingPos);
}
/**
* Returns true if the block of the passed blockstate is a Hay block, otherwise false.
*/
private boolean isHayBlock(BlockState stateIn) {
return stateIn.getBlock() == Blocks.HAY_BLOCK;
}
/**
* Amount of light emitted
* @deprecated prefer calling {@link IBlockState#getLightValue()}
*/
public int getLightValue(BlockState state) {
return state.get(LIT) ? super.getLightValue(state) : 0;
}
public VoxelShape getShape(BlockState state, IBlockReader worldIn, BlockPos pos, ISelectionContext context) {
return SHAPE;
}
/**
* The type of render function called. MODEL for mixed tesr and static model, MODELBLOCK_ANIMATED for TESR-only,
* LIQUID for vanilla liquids, INVISIBLE to skip all rendering
* @deprecated call via {@link IBlockState#getRenderType()} whenever possible. Implementing/overriding is fine.
*/
public BlockRenderType getRenderType(BlockState state) {
return BlockRenderType.MODEL;
}
/**
* Gets the render layer this block will render on. SOLID for solid blocks, CUTOUT or CUTOUT_MIPPED for on-off
* transparency (glass, reeds), TRANSLUCENT for fully blended transparency (stained glass)
*/
public BlockRenderLayer getRenderLayer() {
return BlockRenderLayer.CUTOUT;
}
/**
* Called periodically clientside on blocks near the player to show effects (like furnace fire particles). Note that
* this method is unrelated to {@link randomTick} and {@link #needsRandomTick}, and will always be called regardless
* of whether the block can receive random update ticks
*/
@OnlyIn(Dist.CLIENT)
public void animateTick(BlockState stateIn, World worldIn, BlockPos pos, Random rand) {
if (stateIn.get(LIT)) {
if (rand.nextInt(10) == 0) {
worldIn.playSound((double)((float)pos.getX() + 0.5F), (double)((float)pos.getY() + 0.5F), (double)((float)pos.getZ() + 0.5F), SoundEvents.BLOCK_CAMPFIRE_CRACKLE, SoundCategory.BLOCKS, 0.5F + rand.nextFloat(), rand.nextFloat() * 0.7F + 0.6F, false);
}
if (rand.nextInt(5) == 0) {
for(int i = 0; i < rand.nextInt(1) + 1; ++i) {
worldIn.addParticle(ParticleTypes.LAVA, (double)((float)pos.getX() + 0.5F), (double)((float)pos.getY() + 0.5F), (double)((float)pos.getZ() + 0.5F), (double)(rand.nextFloat() / 2.0F), 5.0E-5D, (double)(rand.nextFloat() / 2.0F));
}
}
}
}
public boolean receiveFluid(IWorld worldIn, BlockPos pos, BlockState state, IFluidState fluidStateIn) {
if (!state.get(BlockStateProperties.WATERLOGGED) && fluidStateIn.getFluid() == Fluids.WATER) {
boolean flag = state.get(LIT);
if (flag) {
if (worldIn.isRemote()) {
for(int i = 0; i < 20; ++i) {
func_220098_a(worldIn.getWorld(), pos, state.get(SIGNAL_FIRE), true);
}
} else {
worldIn.playSound((PlayerEntity)null, pos, SoundEvents.ENTITY_GENERIC_EXTINGUISH_FIRE, SoundCategory.BLOCKS, 1.0F, 1.0F);
}
TileEntity tileentity = worldIn.getTileEntity(pos);
if (tileentity instanceof CampfireEntity) {
((CampfireEntity)tileentity).func_213986_d();
}
}
worldIn.setBlockState(pos, state.with(WATERLOGGED, Boolean.valueOf(true)).with(LIT, Boolean.valueOf(false)), 3);
worldIn.getPendingFluidTicks().scheduleTick(pos, fluidStateIn.getFluid(), fluidStateIn.getFluid().getTickRate(worldIn));
return true;
} else {
return false;
}
}
public void onProjectileCollision(World worldIn, BlockState state, BlockRayTraceResult hit, Entity projectile) {
if (!worldIn.isRemote && projectile instanceof AbstractArrowEntity) {
AbstractArrowEntity abstractarrowentity = (AbstractArrowEntity)projectile;
if (abstractarrowentity.isBurning() && !state.get(LIT) && !state.get(WATERLOGGED)) {
BlockPos blockpos = hit.getPos();
worldIn.setBlockState(blockpos, state.with(BlockStateProperties.LIT, Boolean.valueOf(true)), 11);
}
}
}
public static void func_220098_a(World p_220098_0_, BlockPos pos, boolean p_220098_2_, boolean p_220098_3_) {
Random random = p_220098_0_.getRandom();
BasicParticleType basicparticletype = p_220098_2_ ? ParticleTypes.CAMPFIRE_SIGNAL_SMOKE : ParticleTypes.CAMPFIRE_COSY_SMOKE;
p_220098_0_.func_217404_b(basicparticletype, true, (double)pos.getX() + 0.5D + random.nextDouble() / 3.0D * (double)(random.nextBoolean() ? 1 : -1), (double)pos.getY() + random.nextDouble() + random.nextDouble(), (double)pos.getZ() + 0.5D + random.nextDouble() / 3.0D * (double)(random.nextBoolean() ? 1 : -1), 0.0D, 0.07D, 0.0D);
if (p_220098_3_) {
p_220098_0_.addParticle(ParticleTypes.SMOKE, (double)pos.getX() + 0.25D + random.nextDouble() / 2.0D * (double)(random.nextBoolean() ? 1 : -1), (double)pos.getY() + 0.4D, (double)pos.getZ() + 0.25D + random.nextDouble() / 2.0D * (double)(random.nextBoolean() ? 1 : -1), 0.0D, 0.005D, 0.0D);
}
}
public IFluidState getFluidState(BlockState state) {
return state.get(WATERLOGGED) ? Fluids.WATER.getStillFluidState(false) : super.getFluidState(state);
}
/**
* Returns the blockstate with the given rotation from the passed blockstate. If inapplicable, returns the passed
* blockstate.
* @deprecated call via {@link IBlockState#withRotation(Rotation)} whenever possible. Implementing/overriding is
* fine.
*/
public BlockState rotate(BlockState state, Rotation rot) {
return state.with(FACING, rot.rotate(state.get(FACING)));
}
/**
* Returns the blockstate with the given mirror of the passed blockstate. If inapplicable, returns the passed
* blockstate.
* @deprecated call via {@link IBlockState#withMirror(Mirror)} whenever possible. Implementing/overriding is fine.
*/
public BlockState mirror(BlockState state, Mirror mirrorIn) {
return state.rotate(mirrorIn.toRotation(state.get(FACING)));
}
protected void fillStateContainer(StateContainer.Builder<Block, BlockState> builder) {
builder.add(LIT, SIGNAL_FIRE, WATERLOGGED, FACING);
}
public TileEntity createNewTileEntity(IBlockReader worldIn) {
return new CampfireEntity();
}
public boolean allowsMovement(BlockState state, IBlockReader worldIn, BlockPos pos, PathType type) {
return false;
}
}
package mymod;
import java.util.Optional;
import java.util.Random;
import javax.annotation.Nullable;
import net.minecraft.inventory.IClearable;
import net.minecraft.inventory.IInventory;
import net.minecraft.inventory.Inventory;
import net.minecraft.inventory.InventoryHelper;
import net.minecraft.inventory.ItemStackHelper;
import net.minecraft.item.ItemStack;
import net.minecraft.item.crafting.CampfireCookingRecipe;
import net.minecraft.item.crafting.IRecipeType;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.network.play.server.SUpdateTileEntityPacket;
import net.minecraft.particles.ParticleTypes;
import net.minecraft.tileentity.ITickableTileEntity;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.tileentity.TileEntityType;
import net.minecraft.util.Direction;
import net.minecraft.util.NonNullList;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.MathHelper;
import net.minecraft.world.World;
public class CampfireEntity extends TileEntity implements IClearable, ITickableTileEntity {
private final NonNullList<ItemStack> inventory = NonNullList.withSize(4, ItemStack.EMPTY);
private final int[] cookingTimes = new int[4];
private final int[] cookingTotalTimes = new int[4];
public CampfireEntity() {
super(TileEntityType.CAMPFIRE);
}
public void tick() {
boolean flag = this.getBlockState().get(Campfire.LIT);
boolean flag1 = this.world.isRemote;
if (flag1) {
if (flag) {
this.addParticles();
}
} else {
if (flag) {
this.cookAndDrop();
} else {
for(int i = 0; i < this.inventory.size(); ++i) {
if (this.cookingTimes[i] > 0) {
this.cookingTimes[i] = MathHelper.clamp(this.cookingTimes[i] - 2, 0, this.cookingTotalTimes[i]);
}
}
}
}
}
/**
* Individually tracks the cooking of each item, then spawns the finished product in-world and clears the
* corresponding held item.
*/
private void cookAndDrop() {
for(int i = 0; i < this.inventory.size(); ++i) {
ItemStack itemstack = this.inventory.get(i);
if (!itemstack.isEmpty()) {
++this.cookingTimes[i];
if (this.cookingTimes[i] >= this.cookingTotalTimes[i]) {
IInventory iinventory = new Inventory(itemstack);
ItemStack itemstack1 = this.world.getRecipeManager().getRecipe(IRecipeType.CAMPFIRE_COOKING, iinventory, this.world).map((p_213979_1_) -> {
return p_213979_1_.getCraftingResult(iinventory);
}).orElse(itemstack);
BlockPos blockpos = this.getPos();
InventoryHelper.spawnItemStack(this.world, (double)blockpos.getX(), (double)blockpos.getY(), (double)blockpos.getZ(), itemstack1);
this.inventory.set(i, ItemStack.EMPTY);
this.func_213981_s();
}
}
}
}
private void addParticles() {
World world = this.getWorld();
if (world != null) {
BlockPos blockpos = this.getPos();
Random random = world.rand;
if (random.nextFloat() < 0.11F) {
for(int i = 0; i < random.nextInt(2) + 2; ++i) {
Campfire.func_220098_a(world, blockpos, this.getBlockState().get(Campfire.SIGNAL_FIRE), false);
}
}
int l = this.getBlockState().get(Campfire.FACING).getHorizontalIndex();
for(int j = 0; j < this.inventory.size(); ++j) {
if (!this.inventory.get(j).isEmpty() && random.nextFloat() < 0.2F) {
Direction direction = Direction.byHorizontalIndex(Math.floorMod(j + l, 4));
float f = 0.3125F;
double d0 = (double)blockpos.getX() + 0.5D - (double)((float)direction.getXOffset() * 0.3125F) + (double)((float)direction.rotateY().getXOffset() * 0.3125F);
double d1 = (double)blockpos.getY() + 0.5D;
double d2 = (double)blockpos.getZ() + 0.5D - (double)((float)direction.getZOffset() * 0.3125F) + (double)((float)direction.rotateY().getZOffset() * 0.3125F);
for(int k = 0; k < 4; ++k) {
world.addParticle(ParticleTypes.SMOKE, d0, d1, d2, 0.0D, 5.0E-4D, 0.0D);
}
}
}
}
}
/**
* Returns a NonNullList<ItemStack> of items currently held in the campfire.
*/
public NonNullList<ItemStack> getInventory() {
return this.inventory;
}
public void read(CompoundNBT compound) {
super.read(compound);
this.inventory.clear();
ItemStackHelper.loadAllItems(compound, this.inventory);
if (compound.contains("CookingTimes", 11)) {
int[] aint = compound.getIntArray("CookingTimes");
System.arraycopy(aint, 0, this.cookingTimes, 0, Math.min(this.cookingTotalTimes.length, aint.length));
}
if (compound.contains("CookingTotalTimes", 11)) {
int[] aint1 = compound.getIntArray("CookingTotalTimes");
System.arraycopy(aint1, 0, this.cookingTotalTimes, 0, Math.min(this.cookingTotalTimes.length, aint1.length));
}
}
public CompoundNBT write(CompoundNBT compound) {
this.writeItems(compound);
compound.putIntArray("CookingTimes", this.cookingTimes);
compound.putIntArray("CookingTotalTimes", this.cookingTotalTimes);
return compound;
}
private CompoundNBT writeItems(CompoundNBT compound) {
super.write(compound);
ItemStackHelper.saveAllItems(compound, this.inventory, true);
return compound;
}
/**
* Retrieves packet to send to the client whenever this Tile Entity is resynced via World.notifyBlockUpdate. For
* modded TE's, this packet comes back to you clientside in {@link #onDataPacket}
*/
@Nullable
public SUpdateTileEntityPacket getUpdatePacket() {
return new SUpdateTileEntityPacket(this.pos, 13, this.getUpdateTag());
}
/**
* Get an NBT compound to sync to the client with SPacketChunkData, used for initial loading of the chunk or when
* many blocks change at once. This compound comes back to you clientside in {@link handleUpdateTag}
*/
public CompoundNBT getUpdateTag() {
return this.writeItems(new CompoundNBT());
}
public Optional<CampfireCookingRecipe> findMatchingRecipe(ItemStack itemStackIn) {
return this.inventory.stream().noneMatch(ItemStack::isEmpty) ? Optional.empty() : this.world.getRecipeManager().getRecipe(IRecipeType.CAMPFIRE_COOKING, new Inventory(itemStackIn), this.world);
}
public boolean addItem(ItemStack itemStackIn, int cookTime) {
for(int i = 0; i < this.inventory.size(); ++i) {
ItemStack itemstack = this.inventory.get(i);
if (itemstack.isEmpty()) {
this.cookingTotalTimes[i] = cookTime;
this.cookingTimes[i] = 0;
this.inventory.set(i, itemStackIn.split(1));
this.func_213981_s();
return true;
}
}
return false;
}
private void func_213981_s() {
this.markDirty();
this.getWorld().notifyBlockUpdate(this.getPos(), this.getBlockState(), this.getBlockState(), 3);
}
public void clear() {
this.inventory.clear();
}
public void func_213986_d() {
if (!this.getWorld().isRemote) {
InventoryHelper.dropItems(this.getWorld(), this.getPos(), this.getInventory());
}
this.func_213981_s();
}
}
@SubscribeEvent
public static void registerBlocks(final RegistryEvent.Register<Block> event) {
event.getRegistry().registerAll(
BlockList.campfire = new Campfire(Block.Properties.create(Material.WOOD, MaterialColor.OBSIDIAN).hardnessAndResistance(2.0F).sound(SoundType.WOOD).lightValue(15).tickRandomly())
.setRegistryName(location("campfire2"))
);
}
@SubscribeEvent
public static void registerItems(final RegistryEvent.Register<Item> event) {
event.getRegistry().registerAll(
ItemList.campfire = new BlockItem(BlockList.campfire, new Item.Properties().group(ItemGroup.MISC)).setRegistryName(BlockList.campfire.getRegistryName())
);
}