Code/net/minecraft/world/level/block/DispenserBlock.java

185 lines
8.5 KiB
Java

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<DispenserBlock> CODEC = simpleCodec(DispenserBlock::new);
public static final EnumProperty<Direction> FACING = DirectionalBlock.FACING;
public static final BooleanProperty TRIGGERED = BlockStateProperties.TRIGGERED;
private static final DefaultDispenseItemBehavior DEFAULT_BEHAVIOR = new DefaultDispenseItemBehavior();
public static final Map<Item, DispenseItemBehavior> DISPENSER_REGISTRY = new IdentityHashMap<>();
private static final int TRIGGER_DURATION = 4;
@Override
public MapCodec<? extends DispenserBlock> 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<Block, BlockState> p_52719_) {
p_52719_.add(FACING, TRIGGERED);
}
}