package net.minecraft.world.item; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.core.particles.BlockParticleOption; import net.minecraft.core.particles.ParticleTypes; import net.minecraft.server.level.ServerLevel; import net.minecraft.sounds.SoundEvent; import net.minecraft.sounds.SoundEvents; import net.minecraft.sounds.SoundSource; import net.minecraft.world.InteractionHand; import net.minecraft.world.InteractionResult; import net.minecraft.world.entity.EntitySelector; import net.minecraft.world.entity.EquipmentSlot; import net.minecraft.world.entity.HumanoidArm; import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.player.Player; import net.minecraft.world.entity.projectile.ProjectileUtil; import net.minecraft.world.item.context.UseOnContext; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.BrushableBlock; import net.minecraft.world.level.block.RenderShape; import net.minecraft.world.level.block.entity.BrushableBlockEntity; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.phys.BlockHitResult; import net.minecraft.world.phys.HitResult; import net.minecraft.world.phys.Vec3; import net.minecraft.world.phys.HitResult.Type; public class BrushItem extends Item { public static final int ANIMATION_DURATION = 10; private static final int USE_DURATION = 200; public BrushItem(Item.Properties properties) { super(properties); } @Override public InteractionResult useOn(UseOnContext context) { Player player = context.getPlayer(); if (player != null && this.calculateHitResult(player).getType() == Type.BLOCK) { player.startUsingItem(context.getHand()); } return InteractionResult.CONSUME; } @Override public ItemUseAnimation getUseAnimation(ItemStack stack) { return ItemUseAnimation.BRUSH; } @Override public int getUseDuration(ItemStack stack, LivingEntity entity) { return 200; } @Override public void onUseTick(Level level, LivingEntity livingEntity, ItemStack stack, int remainingUseDuration) { if (remainingUseDuration >= 0 && livingEntity instanceof Player player) { HitResult hitResult = this.calculateHitResult(player); if (hitResult instanceof BlockHitResult blockHitResult && hitResult.getType() == Type.BLOCK) { int i = this.getUseDuration(stack, livingEntity) - remainingUseDuration + 1; boolean bl = i % 10 == 5; if (bl) { BlockPos blockPos = blockHitResult.getBlockPos(); BlockState blockState = level.getBlockState(blockPos); HumanoidArm humanoidArm = livingEntity.getUsedItemHand() == InteractionHand.MAIN_HAND ? player.getMainArm() : player.getMainArm().getOpposite(); if (blockState.shouldSpawnTerrainParticles() && blockState.getRenderShape() != RenderShape.INVISIBLE) { this.spawnDustParticles(level, blockHitResult, blockState, livingEntity.getViewVector(0.0F), humanoidArm); } SoundEvent soundEvent; if (blockState.getBlock() instanceof BrushableBlock brushableBlock) { soundEvent = brushableBlock.getBrushSound(); } else { soundEvent = SoundEvents.BRUSH_GENERIC; } level.playSound(player, blockPos, soundEvent, SoundSource.BLOCKS); if (level instanceof ServerLevel serverLevel && level.getBlockEntity(blockPos) instanceof BrushableBlockEntity brushableBlockEntity) { boolean bl2 = brushableBlockEntity.brush(level.getGameTime(), serverLevel, player, blockHitResult.getDirection(), stack); if (bl2) { EquipmentSlot equipmentSlot = stack.equals(player.getItemBySlot(EquipmentSlot.OFFHAND)) ? EquipmentSlot.OFFHAND : EquipmentSlot.MAINHAND; stack.hurtAndBreak(1, player, equipmentSlot); } } } } else { livingEntity.releaseUsingItem(); } } else { livingEntity.releaseUsingItem(); } } private HitResult calculateHitResult(Player player) { return ProjectileUtil.getHitResultOnViewVector(player, EntitySelector.CAN_BE_PICKED, player.blockInteractionRange()); } private void spawnDustParticles(Level level, BlockHitResult hitResult, BlockState state, Vec3 pos, HumanoidArm arm) { double d = 3.0; int i = arm == HumanoidArm.RIGHT ? 1 : -1; int j = level.getRandom().nextInt(7, 12); BlockParticleOption blockParticleOption = new BlockParticleOption(ParticleTypes.BLOCK, state); Direction direction = hitResult.getDirection(); BrushItem.DustParticlesDelta dustParticlesDelta = BrushItem.DustParticlesDelta.fromDirection(pos, direction); Vec3 vec3 = hitResult.getLocation(); for (int k = 0; k < j; k++) { level.addParticle( blockParticleOption, vec3.x - (direction == Direction.WEST ? 1.0E-6F : 0.0F), vec3.y, vec3.z - (direction == Direction.NORTH ? 1.0E-6F : 0.0F), dustParticlesDelta.xd() * i * 3.0 * level.getRandom().nextDouble(), 0.0, dustParticlesDelta.zd() * i * 3.0 * level.getRandom().nextDouble() ); } } record DustParticlesDelta(double xd, double yd, double zd) { private static final double ALONG_SIDE_DELTA = 1.0; private static final double OUT_FROM_SIDE_DELTA = 0.1; public static BrushItem.DustParticlesDelta fromDirection(Vec3 pos, Direction direction) { double d = 0.0; return switch (direction) { case DOWN, UP -> new BrushItem.DustParticlesDelta(pos.z(), 0.0, -pos.x()); case NORTH -> new BrushItem.DustParticlesDelta(1.0, 0.0, -0.1); case SOUTH -> new BrushItem.DustParticlesDelta(-1.0, 0.0, 0.1); case WEST -> new BrushItem.DustParticlesDelta(-0.1, 0.0, -1.0); case EAST -> new BrushItem.DustParticlesDelta(0.1, 0.0, 1.0); }; } } }