package net.minecraft.world.entity.monster.creaking; import com.mojang.serialization.Dynamic; import java.util.List; import java.util.function.DoubleSupplier; import java.util.function.Predicate; import net.minecraft.core.BlockPos; import net.minecraft.network.protocol.game.DebugPackets; import net.minecraft.network.syncher.EntityDataAccessor; import net.minecraft.network.syncher.EntityDataSerializers; import net.minecraft.network.syncher.SynchedEntityData; import net.minecraft.network.syncher.SynchedEntityData.Builder; import net.minecraft.server.level.ServerLevel; import net.minecraft.sounds.SoundEvent; import net.minecraft.sounds.SoundEvents; import net.minecraft.util.profiling.Profiler; import net.minecraft.util.profiling.ProfilerFiller; import net.minecraft.world.damagesource.DamageSource; import net.minecraft.world.entity.AnimationState; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.ai.Brain; import net.minecraft.world.entity.ai.Brain.Provider; import net.minecraft.world.entity.ai.attributes.Attributes; import net.minecraft.world.entity.ai.control.BodyRotationControl; import net.minecraft.world.entity.ai.control.JumpControl; import net.minecraft.world.entity.ai.control.LookControl; import net.minecraft.world.entity.ai.control.MoveControl; import net.minecraft.world.entity.ai.memory.MemoryModuleType; import net.minecraft.world.entity.ai.navigation.GroundPathNavigation; import net.minecraft.world.entity.monster.Monster; import net.minecraft.world.entity.player.Player; import net.minecraft.world.level.Level; import net.minecraft.world.level.LevelReader; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.gameevent.GameEvent; import org.jetbrains.annotations.Nullable; public class Creaking extends Monster { private static final EntityDataAccessor CAN_MOVE = SynchedEntityData.defineId(Creaking.class, EntityDataSerializers.BOOLEAN); private static final EntityDataAccessor IS_ACTIVE = SynchedEntityData.defineId(Creaking.class, EntityDataSerializers.BOOLEAN); private static final int ATTACK_ANIMATION_DURATION = 20; private static final int MAX_HEALTH = 1; private static final float ATTACK_DAMAGE = 2.0F; private static final float FOLLOW_RANGE = 32.0F; private static final float ACTIVATION_RANGE_SQ = 144.0F; public static final int ATTACK_INTERVAL = 40; private static final float MOVEMENT_SPEED_WHEN_FIGHTING = 0.3F; public static final float SPEED_MULTIPLIER_WHEN_IDLING = 0.2F; public static final int CREAKING_ORANGE = 16545810; public static final int CREAKING_GRAY = 6250335; private int attackAnimationRemainingTicks; public final AnimationState attackAnimationState = new AnimationState(); public final AnimationState invulnerabilityAnimationState = new AnimationState(); public Creaking(EntityType entityType, Level level) { super(entityType, level); this.lookControl = new Creaking.CreakingLookControl(this); this.moveControl = new Creaking.CreakingMoveControl(this); this.jumpControl = new Creaking.CreakingJumpControl(this); GroundPathNavigation groundPathNavigation = (GroundPathNavigation)this.getNavigation(); groundPathNavigation.setCanFloat(true); this.xpReward = 0; } @Override protected BodyRotationControl createBodyControl() { return new Creaking.CreakingBodyRotationControl(this); } @Override protected Provider brainProvider() { return CreakingAi.brainProvider(); } @Override protected Brain makeBrain(Dynamic dynamic) { return CreakingAi.makeBrain(this.brainProvider().makeBrain(dynamic)); } @Override protected void defineSynchedData(Builder builder) { super.defineSynchedData(builder); builder.define(CAN_MOVE, true); builder.define(IS_ACTIVE, false); } public static net.minecraft.world.entity.ai.attributes.AttributeSupplier.Builder createAttributes() { return Monster.createMonsterAttributes() .add(Attributes.MAX_HEALTH, 1.0) .add(Attributes.MOVEMENT_SPEED, 0.3F) .add(Attributes.ATTACK_DAMAGE, 2.0) .add(Attributes.FOLLOW_RANGE, 32.0) .add(Attributes.STEP_HEIGHT, 1.0); } public boolean canMove() { return this.entityData.get(CAN_MOVE); } @Override public boolean doHurtTarget(ServerLevel serverLevel, Entity entity) { if (!(entity instanceof LivingEntity)) { return false; } else { this.attackAnimationRemainingTicks = 20; this.level().broadcastEntityEvent(this, (byte)4); return super.doHurtTarget(serverLevel, entity); } } @Override public boolean isPushable() { return super.isPushable() && this.canMove(); } @Override public Brain getBrain() { return (Brain)super.getBrain(); } @Override protected void customServerAiStep(ServerLevel serverLevel) { ProfilerFiller profilerFiller = Profiler.get(); profilerFiller.push("creakingBrain"); this.getBrain().tick((ServerLevel)this.level(), this); profilerFiller.pop(); CreakingAi.updateActivity(this); } @Override public void aiStep() { if (this.attackAnimationRemainingTicks > 0) { this.attackAnimationRemainingTicks--; } if (!this.level().isClientSide) { boolean bl = this.entityData.get(CAN_MOVE); boolean bl2 = this.checkCanMove(); if (bl2 != bl) { this.gameEvent(GameEvent.ENTITY_ACTION); if (bl2) { this.makeSound(SoundEvents.CREAKING_UNFREEZE); } else { this.stopInPlace(); this.makeSound(SoundEvents.CREAKING_FREEZE); } } this.entityData.set(CAN_MOVE, bl2); } super.aiStep(); } @Override public void tick() { super.tick(); if (this.level().isClientSide) { this.setupAnimationStates(); } } private void setupAnimationStates() { this.attackAnimationState.animateWhen(this.attackAnimationRemainingTicks > 0, this.tickCount); } @Override public void handleEntityEvent(byte id) { if (id == 4) { this.attackAnimationRemainingTicks = 20; this.playAttackSound(); } else { super.handleEntityEvent(id); } } @Override public void playAttackSound() { this.makeSound(SoundEvents.CREAKING_ATTACK); } @Override protected SoundEvent getAmbientSound() { return this.isActive() ? null : SoundEvents.CREAKING_AMBIENT; } @Override protected SoundEvent getHurtSound(DamageSource damageSource) { return SoundEvents.CREAKING_SWAY; } @Override protected SoundEvent getDeathSound() { return SoundEvents.CREAKING_DEATH; } @Override protected void playStepSound(BlockPos pos, BlockState state) { this.playSound(SoundEvents.CREAKING_STEP, 0.15F, 1.0F); } @Nullable @Override public LivingEntity getTarget() { return this.getTargetFromBrain(); } @Override protected void sendDebugPackets() { super.sendDebugPackets(); DebugPackets.sendEntityBrain(this); } @Override public void knockback(double strength, double x, double z) { if (this.canMove()) { super.knockback(strength, x, z); } } public boolean checkCanMove() { List list = (List)this.brain.getMemory(MemoryModuleType.NEAREST_PLAYERS).orElse(List.of()); if (list.isEmpty()) { if (this.isActive()) { this.gameEvent(GameEvent.ENTITY_ACTION); this.makeSound(SoundEvents.CREAKING_DEACTIVATE); this.setIsActive(false); } return true; } else { Predicate predicate = this.isActive() ? LivingEntity.PLAYER_NOT_WEARING_DISGUISE_ITEM : livingEntity -> true; for (Player player : list) { if (!player.isCreative() && this.isLookingAtMe(player, 0.5, false, true, predicate, new DoubleSupplier[]{this::getEyeY, this::getY, () -> (this.getEyeY() + this.getY()) / 2.0})) { if (this.isActive()) { return false; } if (player.distanceToSqr(this) < 144.0) { this.gameEvent(GameEvent.ENTITY_ACTION); this.makeSound(SoundEvents.CREAKING_ACTIVATE); this.setIsActive(true); this.getBrain().setMemory(MemoryModuleType.ATTACK_TARGET, player); return false; } } } return true; } } public void setIsActive(boolean bl) { this.entityData.set(IS_ACTIVE, bl); } public boolean isActive() { return this.entityData.get(IS_ACTIVE); } @Override public float getWalkTargetValue(BlockPos pos, LevelReader level) { return 0.0F; } class CreakingBodyRotationControl extends BodyRotationControl { public CreakingBodyRotationControl(final Creaking creaking2) { super(creaking2); } @Override public void clientTick() { if (Creaking.this.canMove()) { super.clientTick(); } } } class CreakingJumpControl extends JumpControl { public CreakingJumpControl(final Creaking creaking2) { super(creaking2); } @Override public void tick() { if (Creaking.this.canMove()) { super.tick(); } else { Creaking.this.setJumping(false); } } } class CreakingLookControl extends LookControl { public CreakingLookControl(final Creaking creaking2) { super(creaking2); } @Override public void tick() { if (Creaking.this.canMove()) { super.tick(); } } } class CreakingMoveControl extends MoveControl { public CreakingMoveControl(final Creaking creaking2) { super(creaking2); } @Override public void tick() { if (Creaking.this.canMove()) { super.tick(); } } } }