minecraft-src/net/minecraft/world/level/block/entity/BellBlockEntity.java
2025-07-04 01:41:11 +03:00

176 lines
6.3 KiB
Java

package net.minecraft.world.level.block.entity;
import java.util.List;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.particles.ColorParticleOption;
import net.minecraft.core.particles.ParticleTypes;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.sounds.SoundSource;
import net.minecraft.tags.EntityTypeTags;
import net.minecraft.util.Mth;
import net.minecraft.world.effect.MobEffectInstance;
import net.minecraft.world.effect.MobEffects;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.ai.memory.MemoryModuleType;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.AABB;
import org.apache.commons.lang3.mutable.MutableInt;
public class BellBlockEntity extends BlockEntity {
private static final int DURATION = 50;
private static final int GLOW_DURATION = 60;
private static final int MIN_TICKS_BETWEEN_SEARCHES = 60;
private static final int MAX_RESONATION_TICKS = 40;
private static final int TICKS_BEFORE_RESONATION = 5;
private static final int SEARCH_RADIUS = 48;
private static final int HEAR_BELL_RADIUS = 32;
private static final int HIGHLIGHT_RAIDERS_RADIUS = 48;
private long lastRingTimestamp;
/**
* How many ticks the bell has been ringing.
*/
public int ticks;
public boolean shaking;
public Direction clickDirection;
private List<LivingEntity> nearbyEntities;
private boolean resonating;
/**
* A tick counter before raiders are revealed. At {@link #TICKS_BEFORE_RESONATION} ticks, the resonation sound is played, and at {@link #MAX_RESONATION_TICKS}, nearby raiders are revealed.
*/
private int resonationTicks;
public BellBlockEntity(BlockPos pos, BlockState blockState) {
super(BlockEntityType.BELL, pos, blockState);
}
@Override
public boolean triggerEvent(int id, int type) {
if (id == 1) {
this.updateEntities();
this.resonationTicks = 0;
this.clickDirection = Direction.from3DDataValue(type);
this.ticks = 0;
this.shaking = true;
return true;
} else {
return super.triggerEvent(id, type);
}
}
private static void tick(Level level, BlockPos pos, BlockState state, BellBlockEntity blockEntity, BellBlockEntity.ResonationEndAction resonationEndAction) {
if (blockEntity.shaking) {
blockEntity.ticks++;
}
if (blockEntity.ticks >= 50) {
blockEntity.shaking = false;
blockEntity.ticks = 0;
}
if (blockEntity.ticks >= 5 && blockEntity.resonationTicks == 0 && areRaidersNearby(pos, blockEntity.nearbyEntities)) {
blockEntity.resonating = true;
level.playSound(null, pos, SoundEvents.BELL_RESONATE, SoundSource.BLOCKS, 1.0F, 1.0F);
}
if (blockEntity.resonating) {
if (blockEntity.resonationTicks < 40) {
blockEntity.resonationTicks++;
} else {
resonationEndAction.run(level, pos, blockEntity.nearbyEntities);
blockEntity.resonating = false;
}
}
}
public static void clientTick(Level level, BlockPos pos, BlockState state, BellBlockEntity blockEntity) {
tick(level, pos, state, blockEntity, BellBlockEntity::showBellParticles);
}
public static void serverTick(Level level, BlockPos pos, BlockState state, BellBlockEntity blockEntity) {
tick(level, pos, state, blockEntity, BellBlockEntity::makeRaidersGlow);
}
public void onHit(Direction direction) {
BlockPos blockPos = this.getBlockPos();
this.clickDirection = direction;
if (this.shaking) {
this.ticks = 0;
} else {
this.shaking = true;
}
this.level.blockEvent(blockPos, this.getBlockState().getBlock(), 1, direction.get3DDataValue());
}
private void updateEntities() {
BlockPos blockPos = this.getBlockPos();
if (this.level.getGameTime() > this.lastRingTimestamp + 60L || this.nearbyEntities == null) {
this.lastRingTimestamp = this.level.getGameTime();
AABB aABB = new AABB(blockPos).inflate(48.0);
this.nearbyEntities = this.level.getEntitiesOfClass(LivingEntity.class, aABB);
}
if (!this.level.isClientSide) {
for (LivingEntity livingEntity : this.nearbyEntities) {
if (livingEntity.isAlive() && !livingEntity.isRemoved() && blockPos.closerToCenterThan(livingEntity.position(), 32.0)) {
livingEntity.getBrain().setMemory(MemoryModuleType.HEARD_BELL_TIME, this.level.getGameTime());
}
}
}
}
private static boolean areRaidersNearby(BlockPos pos, List<LivingEntity> raiders) {
for (LivingEntity livingEntity : raiders) {
if (livingEntity.isAlive()
&& !livingEntity.isRemoved()
&& pos.closerToCenterThan(livingEntity.position(), 32.0)
&& livingEntity.getType().is(EntityTypeTags.RAIDERS)) {
return true;
}
}
return false;
}
private static void makeRaidersGlow(Level level, BlockPos pos, List<LivingEntity> raiders) {
raiders.stream().filter(livingEntity -> isRaiderWithinRange(pos, livingEntity)).forEach(BellBlockEntity::glow);
}
private static void showBellParticles(Level level, BlockPos pos, List<LivingEntity> raiders) {
MutableInt mutableInt = new MutableInt(16700985);
int i = (int)raiders.stream().filter(livingEntity -> pos.closerToCenterThan(livingEntity.position(), 48.0)).count();
raiders.stream()
.filter(livingEntity -> isRaiderWithinRange(pos, livingEntity))
.forEach(
livingEntity -> {
float f = 1.0F;
double d = Math.sqrt(
(livingEntity.getX() - pos.getX()) * (livingEntity.getX() - pos.getX()) + (livingEntity.getZ() - pos.getZ()) * (livingEntity.getZ() - pos.getZ())
);
double e = pos.getX() + 0.5F + 1.0 / d * (livingEntity.getX() - pos.getX());
double g = pos.getZ() + 0.5F + 1.0 / d * (livingEntity.getZ() - pos.getZ());
int j = Mth.clamp((i - 21) / -2, 3, 15);
for (int k = 0; k < j; k++) {
int l = mutableInt.addAndGet(5);
level.addParticle(ColorParticleOption.create(ParticleTypes.ENTITY_EFFECT, l), e, pos.getY() + 0.5F, g, 0.0, 0.0, 0.0);
}
}
);
}
private static boolean isRaiderWithinRange(BlockPos pos, LivingEntity raider) {
return raider.isAlive() && !raider.isRemoved() && pos.closerToCenterThan(raider.position(), 48.0) && raider.getType().is(EntityTypeTags.RAIDERS);
}
private static void glow(LivingEntity entity) {
entity.addEffect(new MobEffectInstance(MobEffects.GLOWING, 60));
}
@FunctionalInterface
interface ResonationEndAction {
void run(Level level, BlockPos blockPos, List<LivingEntity> list);
}
}