package net.minecraft.world.level.block; import com.mojang.logging.LogUtils; import com.mojang.serialization.MapCodec; import java.util.IdentityHashMap; import java.util.Map; import javax.annotation.Nullable; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.core.Position; import net.minecraft.core.component.DataComponents; import net.minecraft.core.dispenser.BlockSource; import net.minecraft.core.dispenser.DefaultDispenseItemBehavior; import net.minecraft.core.dispenser.DispenseItemBehavior; import net.minecraft.core.dispenser.EquipmentDispenseItemBehavior; import net.minecraft.core.dispenser.ProjectileDispenseBehavior; import net.minecraft.server.level.ServerLevel; import net.minecraft.stats.Stats; import net.minecraft.util.RandomSource; import net.minecraft.world.Containers; import net.minecraft.world.InteractionResult; import net.minecraft.world.entity.player.Player; import net.minecraft.world.inventory.AbstractContainerMenu; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.context.BlockPlaceContext; import net.minecraft.world.level.ItemLike; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.entity.DispenserBlockEntity; import net.minecraft.world.level.block.entity.DropperBlockEntity; import net.minecraft.world.level.block.state.BlockBehaviour; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.StateDefinition; import net.minecraft.world.level.block.state.properties.BlockStateProperties; import net.minecraft.world.level.block.state.properties.BooleanProperty; import net.minecraft.world.level.block.state.properties.EnumProperty; import net.minecraft.world.level.gameevent.GameEvent; import net.minecraft.world.level.redstone.Orientation; import net.minecraft.world.phys.BlockHitResult; import net.minecraft.world.phys.Vec3; import org.slf4j.Logger; public class DispenserBlock extends BaseEntityBlock { private static final Logger LOGGER = LogUtils.getLogger(); public static final MapCodec CODEC = simpleCodec(DispenserBlock::new); public static final EnumProperty FACING = DirectionalBlock.FACING; public static final BooleanProperty TRIGGERED = BlockStateProperties.TRIGGERED; private static final DefaultDispenseItemBehavior DEFAULT_BEHAVIOR = new DefaultDispenseItemBehavior(); public static final Map DISPENSER_REGISTRY = new IdentityHashMap<>(); private static final int TRIGGER_DURATION = 4; @Override public MapCodec codec() { return CODEC; } public static void registerBehavior(ItemLike p_52673_, DispenseItemBehavior p_52674_) { DISPENSER_REGISTRY.put(p_52673_.asItem(), p_52674_); } public static void registerProjectileBehavior(ItemLike p_329878_) { DISPENSER_REGISTRY.put(p_329878_.asItem(), new ProjectileDispenseBehavior(p_329878_.asItem())); } protected DispenserBlock(BlockBehaviour.Properties p_52664_) { super(p_52664_); this.registerDefaultState(this.stateDefinition.any().setValue(FACING, Direction.NORTH).setValue(TRIGGERED, false)); } @Override protected InteractionResult useWithoutItem(BlockState p_52693_, Level p_52694_, BlockPos p_52695_, Player p_52696_, BlockHitResult p_52698_) { if (!p_52694_.isClientSide && p_52694_.getBlockEntity(p_52695_) instanceof DispenserBlockEntity dispenserblockentity) { p_52696_.openMenu(dispenserblockentity); p_52696_.awardStat(dispenserblockentity instanceof DropperBlockEntity ? Stats.INSPECT_DROPPER : Stats.INSPECT_DISPENSER); } return InteractionResult.SUCCESS; } protected void dispenseFrom(ServerLevel p_52665_, BlockState p_301828_, BlockPos p_52666_) { DispenserBlockEntity dispenserblockentity = p_52665_.getBlockEntity(p_52666_, BlockEntityType.DISPENSER).orElse(null); if (dispenserblockentity == null) { LOGGER.warn("Ignoring dispensing attempt for Dispenser without matching block entity at {}", p_52666_); } else { BlockSource blocksource = new BlockSource(p_52665_, p_52666_, p_301828_, dispenserblockentity); int i = dispenserblockentity.getRandomSlot(p_52665_.random); if (i < 0) { p_52665_.levelEvent(1001, p_52666_, 0); p_52665_.gameEvent(GameEvent.BLOCK_ACTIVATE, p_52666_, GameEvent.Context.of(dispenserblockentity.getBlockState())); } else { ItemStack itemstack = dispenserblockentity.getItem(i); DispenseItemBehavior dispenseitembehavior = this.getDispenseMethod(p_52665_, itemstack); if (dispenseitembehavior != DispenseItemBehavior.NOOP) { dispenserblockentity.setItem(i, dispenseitembehavior.dispense(blocksource, itemstack)); } } } } protected DispenseItemBehavior getDispenseMethod(Level p_328928_, ItemStack p_52667_) { if (!p_52667_.isItemEnabled(p_328928_.enabledFeatures())) { return DEFAULT_BEHAVIOR; } else { DispenseItemBehavior dispenseitembehavior = DISPENSER_REGISTRY.get(p_52667_.getItem()); return dispenseitembehavior != null ? dispenseitembehavior : getDefaultDispenseMethod(p_52667_); } } private static DispenseItemBehavior getDefaultDispenseMethod(ItemStack p_368297_) { return (DispenseItemBehavior)(p_368297_.has(DataComponents.EQUIPPABLE) ? EquipmentDispenseItemBehavior.INSTANCE : DEFAULT_BEHAVIOR); } @Override protected void neighborChanged(BlockState p_52700_, Level p_52701_, BlockPos p_52702_, Block p_52703_, @Nullable Orientation p_365036_, boolean p_52705_) { boolean flag = p_52701_.hasNeighborSignal(p_52702_) || p_52701_.hasNeighborSignal(p_52702_.above()); boolean flag1 = p_52700_.getValue(TRIGGERED); if (flag && !flag1) { p_52701_.scheduleTick(p_52702_, this, 4); p_52701_.setBlock(p_52702_, p_52700_.setValue(TRIGGERED, true), 2); } else if (!flag && flag1) { p_52701_.setBlock(p_52702_, p_52700_.setValue(TRIGGERED, false), 2); } } @Override protected void tick(BlockState p_221075_, ServerLevel p_221076_, BlockPos p_221077_, RandomSource p_221078_) { this.dispenseFrom(p_221076_, p_221075_, p_221077_); } @Override public BlockEntity newBlockEntity(BlockPos p_153162_, BlockState p_153163_) { return new DispenserBlockEntity(p_153162_, p_153163_); } @Override public BlockState getStateForPlacement(BlockPlaceContext p_52669_) { return this.defaultBlockState().setValue(FACING, p_52669_.getNearestLookingDirection().getOpposite()); } @Override protected void affectNeighborsAfterRemoval(BlockState p_394013_, ServerLevel p_392148_, BlockPos p_397022_, boolean p_397547_) { Containers.updateNeighboursAfterDestroy(p_394013_, p_392148_, p_397022_); } public static Position getDispensePosition(BlockSource p_52721_) { return getDispensePosition(p_52721_, 0.7, Vec3.ZERO); } public static Position getDispensePosition(BlockSource p_330786_, double p_333084_, Vec3 p_335028_) { Direction direction = p_330786_.state().getValue(FACING); return p_330786_.center() .add( p_333084_ * direction.getStepX() + p_335028_.x(), p_333084_ * direction.getStepY() + p_335028_.y(), p_333084_ * direction.getStepZ() + p_335028_.z() ); } @Override protected boolean hasAnalogOutputSignal(BlockState p_52682_) { return true; } @Override protected int getAnalogOutputSignal(BlockState p_52689_, Level p_52690_, BlockPos p_52691_) { return AbstractContainerMenu.getRedstoneSignalFromBlockEntity(p_52690_.getBlockEntity(p_52691_)); } @Override protected BlockState rotate(BlockState p_52716_, Rotation p_52717_) { return p_52716_.setValue(FACING, p_52717_.rotate(p_52716_.getValue(FACING))); } @Override protected BlockState mirror(BlockState p_52713_, Mirror p_52714_) { return p_52713_.rotate(p_52714_.getRotation(p_52713_.getValue(FACING))); } @Override protected void createBlockStateDefinition(StateDefinition.Builder p_52719_) { p_52719_.add(FACING, TRIGGERED); } }