cn.nukkit.block.BlockDispenser Maven / Gradle / Ivy
package cn.nukkit.block;
import cn.nukkit.Player;
import cn.nukkit.api.PowerNukkitDifference;
import cn.nukkit.api.PowerNukkitOnly;
import cn.nukkit.api.Since;
import cn.nukkit.blockentity.BlockEntity;
import cn.nukkit.blockentity.BlockEntityDispenser;
import cn.nukkit.blockentity.BlockEntityEjectable;
import cn.nukkit.blockproperty.BlockProperties;
import cn.nukkit.blockproperty.BooleanBlockProperty;
import cn.nukkit.dispenser.DispenseBehavior;
import cn.nukkit.dispenser.DispenseBehaviorRegister;
import cn.nukkit.dispenser.DropperDispenseBehavior;
import cn.nukkit.dispenser.FlintAndSteelDispenseBehavior;
import cn.nukkit.inventory.ContainerInventory;
import cn.nukkit.inventory.Inventory;
import cn.nukkit.inventory.InventoryHolder;
import cn.nukkit.item.Item;
import cn.nukkit.item.ItemBlock;
import cn.nukkit.item.ItemTool;
import cn.nukkit.level.Level;
import cn.nukkit.level.Sound;
import cn.nukkit.math.BlockFace;
import cn.nukkit.math.Vector3;
import cn.nukkit.nbt.tag.CompoundTag;
import cn.nukkit.nbt.tag.ListTag;
import cn.nukkit.nbt.tag.Tag;
import cn.nukkit.network.protocol.LevelEventPacket;
import cn.nukkit.utils.Faceable;
import cn.nukkit.utils.RedstoneComponent;
import javax.annotation.Nonnull;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Random;
import java.util.concurrent.ThreadLocalRandom;
import static cn.nukkit.blockproperty.CommonBlockProperties.FACING_DIRECTION;
/**
* @author CreeperFace
* @since 15.4.2017
*/
@PowerNukkitDifference(since = "1.4.0.0-PN", info = "Implements BlockEntityHolder only in PowerNukkit")
@PowerNukkitDifference(info = "Implements RedstoneComponent.", since = "1.4.0.0-PN")
public class BlockDispenser extends BlockSolidMeta implements RedstoneComponent, Faceable, BlockEntityHolder {
@PowerNukkitOnly
@Since("1.5.0.0-PN")
public static final BooleanBlockProperty TRIGGERED = new BooleanBlockProperty("triggered_bit", false);
@PowerNukkitOnly
@Since("1.5.0.0-PN")
public static final BlockProperties PROPERTIES = new BlockProperties(FACING_DIRECTION, TRIGGERED);
public BlockDispenser() {
this(0);
}
public BlockDispenser(int meta) {
super(meta);
}
@Override
public boolean hasComparatorInputOverride() {
return true;
}
@Override
public String getName() {
return "Dispenser";
}
@Override
public int getId() {
return DISPENSER;
}
@Since("1.4.0.0-PN")
@PowerNukkitOnly
@Nonnull
@Override
public BlockProperties getProperties() {
return PROPERTIES;
}
@PowerNukkitOnly
@Since("1.4.0.0-PN")
@Nonnull
@Override
public String getBlockEntityType() {
return BlockEntity.DISPENSER;
}
@Override
public double getHardness() {
return 3.5;
}
@Override
public double getResistance() {
return 3.5;
}
@Since("1.4.0.0-PN")
@PowerNukkitOnly
@Nonnull
@Override
public Class extends BlockEntityEjectable> getBlockEntityClass() {
return BlockEntityDispenser.class;
}
@Override
public Item toItem() {
return new ItemBlock(this, 0);
}
@Override
public int getComparatorInputOverride() {
InventoryHolder blockEntity = getBlockEntity();
if (blockEntity != null) {
return ContainerInventory.calculateRedstone(blockEntity.getInventory());
}
return 0;
}
public boolean isTriggered() {
return (this.getDamage() & 8) > 0;
}
public void setTriggered(boolean value) {
int i = 0;
i |= getBlockFace().getIndex();
if (value) {
i |= 8;
}
this.setDamage(i);
}
@Override
public boolean canBeActivated() {
return true;
}
@Override
public boolean onActivate(@Nonnull Item item, Player player) {
if (player == null) {
return false;
}
InventoryHolder blockEntity = getBlockEntity();
if (blockEntity == null) {
return false;
}
player.addWindow(blockEntity.getInventory());
return true;
}
@PowerNukkitDifference(info = "BlockData is implemented.", since = "1.4.0.0-PN")
@Override
public boolean place(@Nonnull Item item, @Nonnull Block block, @Nonnull Block target, @Nonnull BlockFace face, double fx, double fy, double fz, Player player) {
if (player != null) {
if (Math.abs(player.x - this.x) < 2 && Math.abs(player.z - this.z) < 2) {
double y = player.y + player.getEyeHeight();
if (y - this.y > 2) {
this.setDamage(BlockFace.UP.getIndex());
} else if (this.y - y > 0) {
this.setDamage(BlockFace.DOWN.getIndex());
} else {
this.setDamage(player.getHorizontalFacing().getOpposite().getIndex());
}
} else {
this.setDamage(player.getHorizontalFacing().getOpposite().getIndex());
}
}
CompoundTag nbt = new CompoundTag().putList(new ListTag<>("Items"));
if (item.hasCustomName()) {
nbt.putString("CustomName", item.getCustomName());
}
if (item.hasCustomBlockData()) {
Map customData = item.getCustomBlockData().getTags();
for (Map.Entry tag : customData.entrySet()) {
nbt.put(tag.getKey(), tag.getValue());
}
}
return BlockEntityHolder.setBlockAndCreateEntity(this, true, true, nbt) != null;
}
@PowerNukkitDifference(info = "Disables the triggered state, when the block is no longer powered + use #isGettingPower() method.", since = "1.4.0.0-PN")
@Override
public int onUpdate(int type) {
if (!this.level.getServer().isRedstoneEnabled()) {
return 0;
}
if (type == Level.BLOCK_UPDATE_SCHEDULED) {
this.dispense();
return type;
} else if (type == Level.BLOCK_UPDATE_REDSTONE || type == Level.BLOCK_UPDATE_NORMAL) {
boolean triggered = this.isTriggered();
if (this.isGettingPower() && !triggered) {
this.setTriggered(true);
this.level.setBlock(this, this, false, false);
level.scheduleUpdate(this, this, 4);
} else if (!this.isGettingPower() && triggered) {
this.setTriggered(false);
this.level.setBlock(this, this, false, false);
}
return type;
}
return 0;
}
@PowerNukkitOnly
public void dispense() {
InventoryHolder blockEntity = getBlockEntity();
if (blockEntity == null) {
return;
}
Random rand = ThreadLocalRandom.current();
int r = 1;
int slot = -1;
Item target = null;
Inventory inv = blockEntity.getInventory();
for (Entry entry : inv.getContents().entrySet()) {
Item item = entry.getValue();
if (!item.isNull() && rand.nextInt(r++) == 0) {
target = item;
slot = entry.getKey();
}
}
LevelEventPacket pk = new LevelEventPacket();
BlockFace facing = getBlockFace();
pk.x = 0.5f + facing.getXOffset() * 0.7f;
pk.y = 0.5f + facing.getYOffset() * 0.7f;
pk.z = 0.5f + facing.getZOffset() * 0.7f;
if (target == null) {
this.level.addSound(this, Sound.RANDOM_CLICK, 1.0f, 1.2f);
getBlockEntity().setDirty();
return;
} else {
if (!(getDispenseBehavior(target) instanceof DropperDispenseBehavior)
&& !(getDispenseBehavior(target) instanceof FlintAndSteelDispenseBehavior))
this.level.addSound(this, Sound.RANDOM_CLICK, 1.0f, 1.0f);
}
pk.evid = LevelEventPacket.EVENT_PARTICLE_SHOOT;
pk.data = 7;
this.level.addChunkPacket(getChunkX(), getChunkZ(), pk);
Item origin = target;
target = target.clone();
DispenseBehavior behavior = getDispenseBehavior(target);
Item result = behavior.dispense(this, facing, target);
target.count--;
inv.setItem(slot, target);
if (result != null) {
if (result.getId() != origin.getId() || result.getDamage() != origin.getDamage()) {
Item[] fit = inv.addItem(result);
if (fit.length > 0) {
for (Item drop : fit) {
this.level.dropItem(this, drop);
}
}
} else {
inv.setItem(slot, result);
}
}
}
@PowerNukkitOnly
protected DispenseBehavior getDispenseBehavior(Item item) {
return DispenseBehaviorRegister.getBehavior(item.getId());
}
@Override
public boolean canHarvestWithHand() {
return false;
}
@Override
public int getToolType() {
return ItemTool.TYPE_PICKAXE;
}
@Override
@PowerNukkitOnly
public int getToolTier() {
return ItemTool.TIER_WOODEN;
}
public Vector3 getDispensePosition() {
BlockFace facing = getBlockFace();
return this.add(
0.5 + 0.7 * facing.getXOffset(),
0.5 + 0.7 * facing.getYOffset(),
0.5 + 0.7 * facing.getZOffset()
);
}
@Override
public BlockFace getBlockFace() {
return BlockFace.fromIndex(this.getDamage() & 0x07);
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy