417 lines
17 KiB
Java
417 lines
17 KiB
Java
package net.minecraft.world.level.block.entity;
|
|
|
|
import com.google.common.collect.Lists;
|
|
import com.mojang.serialization.Codec;
|
|
import com.mojang.serialization.codecs.RecordCodecBuilder;
|
|
import com.mojang.serialization.codecs.RecordCodecBuilder.Instance;
|
|
import io.netty.buffer.ByteBuf;
|
|
import java.util.Arrays;
|
|
import java.util.Iterator;
|
|
import java.util.List;
|
|
import javax.annotation.Nullable;
|
|
import net.minecraft.core.BlockPos;
|
|
import net.minecraft.core.Direction;
|
|
import net.minecraft.core.HolderLookup;
|
|
import net.minecraft.core.component.DataComponentGetter;
|
|
import net.minecraft.core.component.DataComponentMap;
|
|
import net.minecraft.core.component.DataComponents;
|
|
import net.minecraft.core.registries.BuiltInRegistries;
|
|
import net.minecraft.nbt.CompoundTag;
|
|
import net.minecraft.network.codec.ByteBufCodecs;
|
|
import net.minecraft.network.codec.StreamCodec;
|
|
import net.minecraft.network.protocol.game.DebugPackets;
|
|
import net.minecraft.sounds.SoundEvents;
|
|
import net.minecraft.sounds.SoundSource;
|
|
import net.minecraft.tags.BlockTags;
|
|
import net.minecraft.tags.EntityTypeTags;
|
|
import net.minecraft.util.VisibleForDebug;
|
|
import net.minecraft.world.entity.Entity;
|
|
import net.minecraft.world.entity.EntitySpawnReason;
|
|
import net.minecraft.world.entity.EntityType;
|
|
import net.minecraft.world.entity.animal.Bee;
|
|
import net.minecraft.world.entity.player.Player;
|
|
import net.minecraft.world.item.component.Bees;
|
|
import net.minecraft.world.item.component.CustomData;
|
|
import net.minecraft.world.level.Level;
|
|
import net.minecraft.world.level.block.BeehiveBlock;
|
|
import net.minecraft.world.level.block.CampfireBlock;
|
|
import net.minecraft.world.level.block.FireBlock;
|
|
import net.minecraft.world.level.block.state.BlockBehaviour;
|
|
import net.minecraft.world.level.block.state.BlockState;
|
|
import net.minecraft.world.level.gameevent.GameEvent;
|
|
|
|
public class BeehiveBlockEntity extends BlockEntity {
|
|
private static final String TAG_FLOWER_POS = "flower_pos";
|
|
private static final String BEES = "bees";
|
|
static final List<String> IGNORED_BEE_TAGS = Arrays.asList(
|
|
"Air",
|
|
"drop_chances",
|
|
"ArmorItems",
|
|
"Brain",
|
|
"CanPickUpLoot",
|
|
"DeathTime",
|
|
"fall_distance",
|
|
"FallFlying",
|
|
"Fire",
|
|
"HandItems",
|
|
"HurtByTimestamp",
|
|
"HurtTime",
|
|
"LeftHanded",
|
|
"Motion",
|
|
"NoGravity",
|
|
"OnGround",
|
|
"PortalCooldown",
|
|
"Pos",
|
|
"Rotation",
|
|
"SleepingX",
|
|
"SleepingY",
|
|
"SleepingZ",
|
|
"CannotEnterHiveTicks",
|
|
"TicksSincePollination",
|
|
"CropsGrownSincePollination",
|
|
"hive_pos",
|
|
"Passengers",
|
|
"leash",
|
|
"UUID"
|
|
);
|
|
public static final int MAX_OCCUPANTS = 3;
|
|
private static final int MIN_TICKS_BEFORE_REENTERING_HIVE = 400;
|
|
private static final int MIN_OCCUPATION_TICKS_NECTAR = 2400;
|
|
public static final int MIN_OCCUPATION_TICKS_NECTARLESS = 600;
|
|
private final List<BeehiveBlockEntity.BeeData> stored = Lists.newArrayList();
|
|
@Nullable
|
|
private BlockPos savedFlowerPos;
|
|
|
|
public BeehiveBlockEntity(BlockPos p_155134_, BlockState p_155135_) {
|
|
super(BlockEntityType.BEEHIVE, p_155134_, p_155135_);
|
|
}
|
|
|
|
@Override
|
|
public void setChanged() {
|
|
if (this.isFireNearby()) {
|
|
this.emptyAllLivingFromHive(null, this.level.getBlockState(this.getBlockPos()), BeehiveBlockEntity.BeeReleaseStatus.EMERGENCY);
|
|
}
|
|
|
|
super.setChanged();
|
|
}
|
|
|
|
public boolean isFireNearby() {
|
|
if (this.level == null) {
|
|
return false;
|
|
} else {
|
|
for (BlockPos blockpos : BlockPos.betweenClosed(this.worldPosition.offset(-1, -1, -1), this.worldPosition.offset(1, 1, 1))) {
|
|
if (this.level.getBlockState(blockpos).getBlock() instanceof FireBlock) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public boolean isEmpty() {
|
|
return this.stored.isEmpty();
|
|
}
|
|
|
|
public boolean isFull() {
|
|
return this.stored.size() == 3;
|
|
}
|
|
|
|
public void emptyAllLivingFromHive(@Nullable Player p_58749_, BlockState p_58750_, BeehiveBlockEntity.BeeReleaseStatus p_58751_) {
|
|
List<Entity> list = this.releaseAllOccupants(p_58750_, p_58751_);
|
|
if (p_58749_ != null) {
|
|
for (Entity entity : list) {
|
|
if (entity instanceof Bee bee && p_58749_.position().distanceToSqr(entity.position()) <= 16.0) {
|
|
if (!this.isSedated()) {
|
|
bee.setTarget(p_58749_);
|
|
} else {
|
|
bee.setStayOutOfHiveCountdown(400);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private List<Entity> releaseAllOccupants(BlockState p_58760_, BeehiveBlockEntity.BeeReleaseStatus p_58761_) {
|
|
List<Entity> list = Lists.newArrayList();
|
|
this.stored.removeIf(p_327282_ -> releaseOccupant(this.level, this.worldPosition, p_58760_, p_327282_.toOccupant(), list, p_58761_, this.savedFlowerPos));
|
|
if (!list.isEmpty()) {
|
|
super.setChanged();
|
|
}
|
|
|
|
return list;
|
|
}
|
|
|
|
@VisibleForDebug
|
|
public int getOccupantCount() {
|
|
return this.stored.size();
|
|
}
|
|
|
|
public static int getHoneyLevel(BlockState p_58753_) {
|
|
return p_58753_.getValue(BeehiveBlock.HONEY_LEVEL);
|
|
}
|
|
|
|
@VisibleForDebug
|
|
public boolean isSedated() {
|
|
return CampfireBlock.isSmokeyPos(this.level, this.getBlockPos());
|
|
}
|
|
|
|
public void addOccupant(Bee p_377433_) {
|
|
if (this.stored.size() < 3) {
|
|
p_377433_.stopRiding();
|
|
p_377433_.ejectPassengers();
|
|
p_377433_.dropLeash();
|
|
this.storeBee(BeehiveBlockEntity.Occupant.of(p_377433_));
|
|
if (this.level != null) {
|
|
if (p_377433_.hasSavedFlowerPos() && (!this.hasSavedFlowerPos() || this.level.random.nextBoolean())) {
|
|
this.savedFlowerPos = p_377433_.getSavedFlowerPos();
|
|
}
|
|
|
|
BlockPos blockpos = this.getBlockPos();
|
|
this.level
|
|
.playSound(null, blockpos.getX(), blockpos.getY(), blockpos.getZ(), SoundEvents.BEEHIVE_ENTER, SoundSource.BLOCKS, 1.0F, 1.0F);
|
|
this.level.gameEvent(GameEvent.BLOCK_CHANGE, blockpos, GameEvent.Context.of(p_377433_, this.getBlockState()));
|
|
}
|
|
|
|
p_377433_.discard();
|
|
super.setChanged();
|
|
}
|
|
}
|
|
|
|
public void storeBee(BeehiveBlockEntity.Occupant p_329282_) {
|
|
this.stored.add(new BeehiveBlockEntity.BeeData(p_329282_));
|
|
}
|
|
|
|
private static boolean releaseOccupant(
|
|
Level p_155137_,
|
|
BlockPos p_155138_,
|
|
BlockState p_155139_,
|
|
BeehiveBlockEntity.Occupant p_335681_,
|
|
@Nullable List<Entity> p_155141_,
|
|
BeehiveBlockEntity.BeeReleaseStatus p_155142_,
|
|
@Nullable BlockPos p_155143_
|
|
) {
|
|
if (Bee.isNightOrRaining(p_155137_) && p_155142_ != BeehiveBlockEntity.BeeReleaseStatus.EMERGENCY) {
|
|
return false;
|
|
} else {
|
|
Direction direction = p_155139_.getValue(BeehiveBlock.FACING);
|
|
BlockPos blockpos = p_155138_.relative(direction);
|
|
boolean flag = !p_155137_.getBlockState(blockpos).getCollisionShape(p_155137_, blockpos).isEmpty();
|
|
if (flag && p_155142_ != BeehiveBlockEntity.BeeReleaseStatus.EMERGENCY) {
|
|
return false;
|
|
} else {
|
|
Entity entity = p_335681_.createEntity(p_155137_, p_155138_);
|
|
if (entity != null) {
|
|
if (entity instanceof Bee bee) {
|
|
if (p_155143_ != null && !bee.hasSavedFlowerPos() && p_155137_.random.nextFloat() < 0.9F) {
|
|
bee.setSavedFlowerPos(p_155143_);
|
|
}
|
|
|
|
if (p_155142_ == BeehiveBlockEntity.BeeReleaseStatus.HONEY_DELIVERED) {
|
|
bee.dropOffNectar();
|
|
if (p_155139_.is(BlockTags.BEEHIVES, p_202037_ -> p_202037_.hasProperty(BeehiveBlock.HONEY_LEVEL))) {
|
|
int i = getHoneyLevel(p_155139_);
|
|
if (i < 5) {
|
|
int j = p_155137_.random.nextInt(100) == 0 ? 2 : 1;
|
|
if (i + j > 5) {
|
|
j--;
|
|
}
|
|
|
|
p_155137_.setBlockAndUpdate(p_155138_, p_155139_.setValue(BeehiveBlock.HONEY_LEVEL, i + j));
|
|
}
|
|
}
|
|
}
|
|
|
|
if (p_155141_ != null) {
|
|
p_155141_.add(bee);
|
|
}
|
|
|
|
float f = entity.getBbWidth();
|
|
double d3 = flag ? 0.0 : 0.55 + f / 2.0F;
|
|
double d0 = p_155138_.getX() + 0.5 + d3 * direction.getStepX();
|
|
double d1 = p_155138_.getY() + 0.5 - entity.getBbHeight() / 2.0F;
|
|
double d2 = p_155138_.getZ() + 0.5 + d3 * direction.getStepZ();
|
|
entity.snapTo(d0, d1, d2, entity.getYRot(), entity.getXRot());
|
|
}
|
|
|
|
p_155137_.playSound(null, p_155138_, SoundEvents.BEEHIVE_EXIT, SoundSource.BLOCKS, 1.0F, 1.0F);
|
|
p_155137_.gameEvent(GameEvent.BLOCK_CHANGE, p_155138_, GameEvent.Context.of(entity, p_155137_.getBlockState(p_155138_)));
|
|
return p_155137_.addFreshEntity(entity);
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private boolean hasSavedFlowerPos() {
|
|
return this.savedFlowerPos != null;
|
|
}
|
|
|
|
private static void tickOccupants(
|
|
Level p_155150_, BlockPos p_155151_, BlockState p_155152_, List<BeehiveBlockEntity.BeeData> p_155153_, @Nullable BlockPos p_155154_
|
|
) {
|
|
boolean flag = false;
|
|
Iterator<BeehiveBlockEntity.BeeData> iterator = p_155153_.iterator();
|
|
|
|
while (iterator.hasNext()) {
|
|
BeehiveBlockEntity.BeeData beehiveblockentity$beedata = iterator.next();
|
|
if (beehiveblockentity$beedata.tick()) {
|
|
BeehiveBlockEntity.BeeReleaseStatus beehiveblockentity$beereleasestatus = beehiveblockentity$beedata.hasNectar()
|
|
? BeehiveBlockEntity.BeeReleaseStatus.HONEY_DELIVERED
|
|
: BeehiveBlockEntity.BeeReleaseStatus.BEE_RELEASED;
|
|
if (releaseOccupant(p_155150_, p_155151_, p_155152_, beehiveblockentity$beedata.toOccupant(), null, beehiveblockentity$beereleasestatus, p_155154_)) {
|
|
flag = true;
|
|
iterator.remove();
|
|
}
|
|
}
|
|
}
|
|
|
|
if (flag) {
|
|
setChanged(p_155150_, p_155151_, p_155152_);
|
|
}
|
|
}
|
|
|
|
public static void serverTick(Level p_155145_, BlockPos p_155146_, BlockState p_155147_, BeehiveBlockEntity p_155148_) {
|
|
tickOccupants(p_155145_, p_155146_, p_155147_, p_155148_.stored, p_155148_.savedFlowerPos);
|
|
if (!p_155148_.stored.isEmpty() && p_155145_.getRandom().nextDouble() < 0.005) {
|
|
double d0 = p_155146_.getX() + 0.5;
|
|
double d1 = p_155146_.getY();
|
|
double d2 = p_155146_.getZ() + 0.5;
|
|
p_155145_.playSound(null, d0, d1, d2, SoundEvents.BEEHIVE_WORK, SoundSource.BLOCKS, 1.0F, 1.0F);
|
|
}
|
|
|
|
DebugPackets.sendHiveInfo(p_155145_, p_155146_, p_155147_, p_155148_);
|
|
}
|
|
|
|
@Override
|
|
protected void loadAdditional(CompoundTag p_333420_, HolderLookup.Provider p_335311_) {
|
|
super.loadAdditional(p_333420_, p_335311_);
|
|
this.stored.clear();
|
|
p_333420_.read("bees", BeehiveBlockEntity.Occupant.LIST_CODEC).orElse(List.of()).forEach(this::storeBee);
|
|
this.savedFlowerPos = p_333420_.read("flower_pos", BlockPos.CODEC).orElse(null);
|
|
}
|
|
|
|
@Override
|
|
protected void saveAdditional(CompoundTag p_187467_, HolderLookup.Provider p_332762_) {
|
|
super.saveAdditional(p_187467_, p_332762_);
|
|
p_187467_.store("bees", BeehiveBlockEntity.Occupant.LIST_CODEC, this.getBees());
|
|
p_187467_.storeNullable("flower_pos", BlockPos.CODEC, this.savedFlowerPos);
|
|
}
|
|
|
|
@Override
|
|
protected void applyImplicitComponents(DataComponentGetter p_395385_) {
|
|
super.applyImplicitComponents(p_395385_);
|
|
this.stored.clear();
|
|
List<BeehiveBlockEntity.Occupant> list = p_395385_.getOrDefault(DataComponents.BEES, Bees.EMPTY).bees();
|
|
list.forEach(this::storeBee);
|
|
}
|
|
|
|
@Override
|
|
protected void collectImplicitComponents(DataComponentMap.Builder p_328977_) {
|
|
super.collectImplicitComponents(p_328977_);
|
|
p_328977_.set(DataComponents.BEES, new Bees(this.getBees()));
|
|
}
|
|
|
|
@Override
|
|
public void removeComponentsFromTag(CompoundTag p_329874_) {
|
|
super.removeComponentsFromTag(p_329874_);
|
|
p_329874_.remove("bees");
|
|
}
|
|
|
|
private List<BeehiveBlockEntity.Occupant> getBees() {
|
|
return this.stored.stream().map(BeehiveBlockEntity.BeeData::toOccupant).toList();
|
|
}
|
|
|
|
static class BeeData {
|
|
private final BeehiveBlockEntity.Occupant occupant;
|
|
private int ticksInHive;
|
|
|
|
BeeData(BeehiveBlockEntity.Occupant p_336059_) {
|
|
this.occupant = p_336059_;
|
|
this.ticksInHive = p_336059_.ticksInHive();
|
|
}
|
|
|
|
public boolean tick() {
|
|
return this.ticksInHive++ > this.occupant.minTicksInHive;
|
|
}
|
|
|
|
public BeehiveBlockEntity.Occupant toOccupant() {
|
|
return new BeehiveBlockEntity.Occupant(this.occupant.entityData, this.ticksInHive, this.occupant.minTicksInHive);
|
|
}
|
|
|
|
public boolean hasNectar() {
|
|
return this.occupant.entityData.getUnsafe().getBooleanOr("HasNectar", false);
|
|
}
|
|
}
|
|
|
|
public static enum BeeReleaseStatus {
|
|
HONEY_DELIVERED,
|
|
BEE_RELEASED,
|
|
EMERGENCY;
|
|
}
|
|
|
|
public record Occupant(CustomData entityData, int ticksInHive, int minTicksInHive) {
|
|
public static final Codec<BeehiveBlockEntity.Occupant> CODEC = RecordCodecBuilder.create(
|
|
p_330401_ -> p_330401_.group(
|
|
CustomData.CODEC.optionalFieldOf("entity_data", CustomData.EMPTY).forGetter(BeehiveBlockEntity.Occupant::entityData),
|
|
Codec.INT.fieldOf("ticks_in_hive").forGetter(BeehiveBlockEntity.Occupant::ticksInHive),
|
|
Codec.INT.fieldOf("min_ticks_in_hive").forGetter(BeehiveBlockEntity.Occupant::minTicksInHive)
|
|
)
|
|
.apply(p_330401_, BeehiveBlockEntity.Occupant::new)
|
|
);
|
|
public static final Codec<List<BeehiveBlockEntity.Occupant>> LIST_CODEC = CODEC.listOf();
|
|
public static final StreamCodec<ByteBuf, BeehiveBlockEntity.Occupant> STREAM_CODEC = StreamCodec.composite(
|
|
CustomData.STREAM_CODEC,
|
|
BeehiveBlockEntity.Occupant::entityData,
|
|
ByteBufCodecs.VAR_INT,
|
|
BeehiveBlockEntity.Occupant::ticksInHive,
|
|
ByteBufCodecs.VAR_INT,
|
|
BeehiveBlockEntity.Occupant::minTicksInHive,
|
|
BeehiveBlockEntity.Occupant::new
|
|
);
|
|
|
|
public static BeehiveBlockEntity.Occupant of(Entity p_331052_) {
|
|
CompoundTag compoundtag = new CompoundTag();
|
|
p_331052_.save(compoundtag);
|
|
BeehiveBlockEntity.IGNORED_BEE_TAGS.forEach(compoundtag::remove);
|
|
boolean flag = compoundtag.getBooleanOr("HasNectar", false);
|
|
return new BeehiveBlockEntity.Occupant(CustomData.of(compoundtag), 0, flag ? 2400 : 600);
|
|
}
|
|
|
|
public static BeehiveBlockEntity.Occupant create(int p_330047_) {
|
|
CompoundTag compoundtag = new CompoundTag();
|
|
compoundtag.putString("id", BuiltInRegistries.ENTITY_TYPE.getKey(EntityType.BEE).toString());
|
|
return new BeehiveBlockEntity.Occupant(CustomData.of(compoundtag), p_330047_, 600);
|
|
}
|
|
|
|
@Nullable
|
|
public Entity createEntity(Level p_328931_, BlockPos p_336164_) {
|
|
CompoundTag compoundtag = this.entityData.copyTag();
|
|
BeehiveBlockEntity.IGNORED_BEE_TAGS.forEach(compoundtag::remove);
|
|
Entity entity = EntityType.loadEntityRecursive(compoundtag, p_328931_, EntitySpawnReason.LOAD, p_334152_ -> p_334152_);
|
|
if (entity != null && entity.getType().is(EntityTypeTags.BEEHIVE_INHABITORS)) {
|
|
entity.setNoGravity(true);
|
|
if (entity instanceof Bee bee) {
|
|
bee.setHivePos(p_336164_);
|
|
setBeeReleaseData(this.ticksInHive, bee);
|
|
}
|
|
|
|
return entity;
|
|
} else {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
private static void setBeeReleaseData(int p_330253_, Bee p_331091_) {
|
|
int i = p_331091_.getAge();
|
|
if (i < 0) {
|
|
p_331091_.setAge(Math.min(0, i + p_330253_));
|
|
} else if (i > 0) {
|
|
p_331091_.setAge(Math.max(0, i - p_330253_));
|
|
}
|
|
|
|
p_331091_.setInLoveTime(Math.max(0, p_331091_.getInLoveTime() - p_330253_));
|
|
}
|
|
}
|
|
} |