2025-11-24 22:52:51 +03:00

742 lines
28 KiB
Java

/*
* Decompiled with CFR 0.152.
*
* Could not load the following classes:
* org.joml.Vector3f
* org.joml.Vector3fc
* org.jspecify.annotations.Nullable
*/
package net.minecraft.world.entity.monster;
import java.util.EnumSet;
import java.util.List;
import java.util.Optional;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.Vec3i;
import net.minecraft.core.component.DataComponentGetter;
import net.minecraft.core.component.DataComponentType;
import net.minecraft.core.component.DataComponents;
import net.minecraft.network.protocol.game.ClientboundAddEntityPacket;
import net.minecraft.network.syncher.EntityDataAccessor;
import net.minecraft.network.syncher.EntityDataSerializers;
import net.minecraft.network.syncher.SynchedEntityData;
import net.minecraft.resources.Identifier;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.sounds.SoundEvent;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.sounds.SoundSource;
import net.minecraft.tags.DamageTypeTags;
import net.minecraft.util.Mth;
import net.minecraft.util.Util;
import net.minecraft.world.Difficulty;
import net.minecraft.world.DifficultyInstance;
import net.minecraft.world.damagesource.DamageSource;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EntitySelector;
import net.minecraft.world.entity.EntitySpawnReason;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.InterpolationHandler;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.Mob;
import net.minecraft.world.entity.MoverType;
import net.minecraft.world.entity.SpawnGroupData;
import net.minecraft.world.entity.ai.attributes.AttributeModifier;
import net.minecraft.world.entity.ai.attributes.AttributeSupplier;
import net.minecraft.world.entity.ai.attributes.Attributes;
import net.minecraft.world.entity.ai.control.BodyRotationControl;
import net.minecraft.world.entity.ai.control.LookControl;
import net.minecraft.world.entity.ai.goal.Goal;
import net.minecraft.world.entity.ai.goal.LookAtPlayerGoal;
import net.minecraft.world.entity.ai.goal.RandomLookAroundGoal;
import net.minecraft.world.entity.ai.goal.target.HurtByTargetGoal;
import net.minecraft.world.entity.ai.goal.target.NearestAttackableTargetGoal;
import net.minecraft.world.entity.animal.AbstractGolem;
import net.minecraft.world.entity.monster.Enemy;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.entity.projectile.AbstractArrow;
import net.minecraft.world.entity.projectile.ShulkerBullet;
import net.minecraft.world.item.DyeColor;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.ServerLevelAccessor;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.gameevent.GameEvent;
import net.minecraft.world.level.storage.ValueInput;
import net.minecraft.world.level.storage.ValueOutput;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3;
import org.joml.Vector3f;
import org.joml.Vector3fc;
import org.jspecify.annotations.Nullable;
public class Shulker
extends AbstractGolem
implements Enemy {
private static final Identifier COVERED_ARMOR_MODIFIER_ID = Identifier.withDefaultNamespace("covered");
private static final AttributeModifier COVERED_ARMOR_MODIFIER = new AttributeModifier(COVERED_ARMOR_MODIFIER_ID, 20.0, AttributeModifier.Operation.ADD_VALUE);
protected static final EntityDataAccessor<Direction> DATA_ATTACH_FACE_ID = SynchedEntityData.defineId(Shulker.class, EntityDataSerializers.DIRECTION);
protected static final EntityDataAccessor<Byte> DATA_PEEK_ID = SynchedEntityData.defineId(Shulker.class, EntityDataSerializers.BYTE);
protected static final EntityDataAccessor<Byte> DATA_COLOR_ID = SynchedEntityData.defineId(Shulker.class, EntityDataSerializers.BYTE);
private static final int TELEPORT_STEPS = 6;
private static final byte NO_COLOR = 16;
private static final byte DEFAULT_COLOR = 16;
private static final int MAX_TELEPORT_DISTANCE = 8;
private static final int OTHER_SHULKER_SCAN_RADIUS = 8;
private static final int OTHER_SHULKER_LIMIT = 5;
private static final float PEEK_PER_TICK = 0.05f;
private static final byte DEFAULT_PEEK = 0;
private static final Direction DEFAULT_ATTACH_FACE = Direction.DOWN;
private static final Vector3f FORWARD = Util.make(() -> {
Vec3i forwardNormal = Direction.SOUTH.getUnitVec3i();
return new Vector3f((float)forwardNormal.getX(), (float)forwardNormal.getY(), (float)forwardNormal.getZ());
});
private static final float MAX_SCALE = 3.0f;
private float currentPeekAmountO;
private float currentPeekAmount;
private @Nullable BlockPos clientOldAttachPosition;
private int clientSideTeleportInterpolation;
private static final float MAX_LID_OPEN = 1.0f;
public Shulker(EntityType<? extends Shulker> type, Level level) {
super((EntityType<? extends AbstractGolem>)type, level);
this.xpReward = 5;
this.lookControl = new ShulkerLookControl(this);
}
@Override
protected void registerGoals() {
this.goalSelector.addGoal(1, new LookAtPlayerGoal(this, Player.class, 8.0f, 0.02f, true));
this.goalSelector.addGoal(4, new ShulkerAttackGoal());
this.goalSelector.addGoal(7, new ShulkerPeekGoal());
this.goalSelector.addGoal(8, new RandomLookAroundGoal(this));
this.targetSelector.addGoal(1, new HurtByTargetGoal(this, this.getClass()).setAlertOthers(new Class[0]));
this.targetSelector.addGoal(2, new ShulkerNearestAttackGoal(this));
this.targetSelector.addGoal(3, new ShulkerDefenseAttackGoal(this));
}
@Override
protected Entity.MovementEmission getMovementEmission() {
return Entity.MovementEmission.NONE;
}
@Override
public SoundSource getSoundSource() {
return SoundSource.HOSTILE;
}
@Override
protected SoundEvent getAmbientSound() {
return SoundEvents.SHULKER_AMBIENT;
}
@Override
public void playAmbientSound() {
if (!this.isClosed()) {
super.playAmbientSound();
}
}
@Override
protected SoundEvent getDeathSound() {
return SoundEvents.SHULKER_DEATH;
}
@Override
protected SoundEvent getHurtSound(DamageSource source) {
if (this.isClosed()) {
return SoundEvents.SHULKER_HURT_CLOSED;
}
return SoundEvents.SHULKER_HURT;
}
@Override
protected void defineSynchedData(SynchedEntityData.Builder entityData) {
super.defineSynchedData(entityData);
entityData.define(DATA_ATTACH_FACE_ID, DEFAULT_ATTACH_FACE);
entityData.define(DATA_PEEK_ID, (byte)0);
entityData.define(DATA_COLOR_ID, (byte)16);
}
public static AttributeSupplier.Builder createAttributes() {
return Mob.createMobAttributes().add(Attributes.MAX_HEALTH, 30.0);
}
@Override
protected BodyRotationControl createBodyControl() {
return new ShulkerBodyRotationControl(this);
}
@Override
protected void readAdditionalSaveData(ValueInput input) {
super.readAdditionalSaveData(input);
this.setAttachFace(input.read("AttachFace", Direction.LEGACY_ID_CODEC).orElse(DEFAULT_ATTACH_FACE));
this.entityData.set(DATA_PEEK_ID, input.getByteOr("Peek", (byte)0));
this.entityData.set(DATA_COLOR_ID, input.getByteOr("Color", (byte)16));
}
@Override
protected void addAdditionalSaveData(ValueOutput output) {
super.addAdditionalSaveData(output);
output.store("AttachFace", Direction.LEGACY_ID_CODEC, this.getAttachFace());
output.putByte("Peek", this.entityData.get(DATA_PEEK_ID));
output.putByte("Color", this.entityData.get(DATA_COLOR_ID));
}
@Override
public void tick() {
super.tick();
if (!(this.level().isClientSide() || this.isPassenger() || this.canStayAt(this.blockPosition(), this.getAttachFace()))) {
this.findNewAttachment();
}
if (this.updatePeekAmount()) {
this.onPeekAmountChange();
}
if (this.level().isClientSide()) {
if (this.clientSideTeleportInterpolation > 0) {
--this.clientSideTeleportInterpolation;
} else {
this.clientOldAttachPosition = null;
}
}
}
private void findNewAttachment() {
Direction attachmentDirection = this.findAttachableSurface(this.blockPosition());
if (attachmentDirection != null) {
this.setAttachFace(attachmentDirection);
} else {
this.teleportSomewhere();
}
}
@Override
protected AABB makeBoundingBox(Vec3 position) {
float physPeek = Shulker.getPhysicalPeek(this.currentPeekAmount);
Direction direction = this.getAttachFace().getOpposite();
return Shulker.getProgressAabb(this.getScale(), direction, physPeek, position);
}
private static float getPhysicalPeek(float amount) {
return 0.5f - Mth.sin((0.5f + amount) * (float)Math.PI) * 0.5f;
}
private boolean updatePeekAmount() {
this.currentPeekAmountO = this.currentPeekAmount;
float targetPeekAmount = (float)this.getRawPeekAmount() * 0.01f;
if (this.currentPeekAmount == targetPeekAmount) {
return false;
}
this.currentPeekAmount = this.currentPeekAmount > targetPeekAmount ? Mth.clamp(this.currentPeekAmount - 0.05f, targetPeekAmount, 1.0f) : Mth.clamp(this.currentPeekAmount + 0.05f, 0.0f, targetPeekAmount);
return true;
}
private void onPeekAmountChange() {
this.reapplyPosition();
float physicalPeek = Shulker.getPhysicalPeek(this.currentPeekAmount);
float physicalPeekOld = Shulker.getPhysicalPeek(this.currentPeekAmountO);
Direction direction = this.getAttachFace().getOpposite();
float push = (physicalPeek - physicalPeekOld) * this.getScale();
if (push <= 0.0f) {
return;
}
List<Entity> entities = this.level().getEntities(this, Shulker.getProgressDeltaAabb(this.getScale(), direction, physicalPeekOld, physicalPeek, this.position()), EntitySelector.NO_SPECTATORS.and(e -> !e.isPassengerOfSameVehicle(this)));
for (Entity entity : entities) {
if (entity instanceof Shulker || entity.noPhysics) continue;
entity.move(MoverType.SHULKER, new Vec3(push * (float)direction.getStepX(), push * (float)direction.getStepY(), push * (float)direction.getStepZ()));
}
}
public static AABB getProgressAabb(float size, Direction direction, float progressTo, Vec3 position) {
return Shulker.getProgressDeltaAabb(size, direction, -1.0f, progressTo, position);
}
public static AABB getProgressDeltaAabb(float size, Direction direction, float progressFrom, float progressTo, Vec3 position) {
AABB boundsAtBottomCenter = new AABB((double)(-size) * 0.5, 0.0, (double)(-size) * 0.5, (double)size * 0.5, size, (double)size * 0.5);
double maxMovement = Math.max(progressFrom, progressTo);
double minMovement = Math.min(progressFrom, progressTo);
AABB aabb = boundsAtBottomCenter.expandTowards((double)direction.getStepX() * maxMovement * (double)size, (double)direction.getStepY() * maxMovement * (double)size, (double)direction.getStepZ() * maxMovement * (double)size).contract((double)(-direction.getStepX()) * (1.0 + minMovement) * (double)size, (double)(-direction.getStepY()) * (1.0 + minMovement) * (double)size, (double)(-direction.getStepZ()) * (1.0 + minMovement) * (double)size);
return aabb.move(position.x, position.y, position.z);
}
@Override
public boolean startRiding(Entity entity, boolean force, boolean sendEventAndTriggers) {
if (this.level().isClientSide()) {
this.clientOldAttachPosition = null;
this.clientSideTeleportInterpolation = 0;
}
this.setAttachFace(Direction.DOWN);
return super.startRiding(entity, force, sendEventAndTriggers);
}
@Override
public void stopRiding() {
super.stopRiding();
if (this.level().isClientSide()) {
this.clientOldAttachPosition = this.blockPosition();
}
this.yBodyRotO = 0.0f;
this.yBodyRot = 0.0f;
}
@Override
public @Nullable SpawnGroupData finalizeSpawn(ServerLevelAccessor level, DifficultyInstance difficulty, EntitySpawnReason spawnReason, @Nullable SpawnGroupData groupData) {
this.setYRot(0.0f);
this.yHeadRot = this.getYRot();
this.setOldPosAndRot();
return super.finalizeSpawn(level, difficulty, spawnReason, groupData);
}
@Override
public void move(MoverType moverType, Vec3 delta) {
if (moverType == MoverType.SHULKER_BOX) {
this.teleportSomewhere();
} else {
super.move(moverType, delta);
}
}
@Override
public Vec3 getDeltaMovement() {
return Vec3.ZERO;
}
@Override
public void setDeltaMovement(Vec3 deltaMovement) {
}
@Override
public void setPos(double x, double y, double z) {
BlockPos oldPos = this.blockPosition();
if (this.isPassenger()) {
super.setPos(x, y, z);
} else {
super.setPos((double)Mth.floor(x) + 0.5, Mth.floor(y + 0.5), (double)Mth.floor(z) + 0.5);
}
if (this.tickCount == 0) {
return;
}
BlockPos pos = this.blockPosition();
if (!pos.equals(oldPos)) {
this.entityData.set(DATA_PEEK_ID, (byte)0);
this.needsSync = true;
if (this.level().isClientSide() && !this.isPassenger() && !pos.equals(this.clientOldAttachPosition)) {
this.clientOldAttachPosition = oldPos;
this.clientSideTeleportInterpolation = 6;
this.xOld = this.getX();
this.yOld = this.getY();
this.zOld = this.getZ();
}
}
}
protected @Nullable Direction findAttachableSurface(BlockPos target) {
for (Direction direction : Direction.values()) {
if (!this.canStayAt(target, direction)) continue;
return direction;
}
return null;
}
private boolean canStayAt(BlockPos target, Direction face) {
if (this.isPositionBlocked(target)) {
return false;
}
Direction oppositeFace = face.getOpposite();
if (!this.level().loadedAndEntityCanStandOnFace(target.relative(face), this, oppositeFace)) {
return false;
}
AABB fullyOpened = Shulker.getProgressAabb(this.getScale(), oppositeFace, 1.0f, target.getBottomCenter()).deflate(1.0E-6);
return this.level().noCollision(this, fullyOpened);
}
private boolean isPositionBlocked(BlockPos target) {
BlockState state = this.level().getBlockState(target);
if (state.isAir()) {
return false;
}
boolean movingPistonInOurCurrentPosition = state.is(Blocks.MOVING_PISTON) && target.equals(this.blockPosition());
return !movingPistonInOurCurrentPosition;
}
protected boolean teleportSomewhere() {
if (this.isNoAi() || !this.isAlive()) {
return false;
}
BlockPos current = this.blockPosition();
for (int attempt = 0; attempt < 5; ++attempt) {
Direction attachmentDirection;
BlockPos target = current.offset(Mth.randomBetweenInclusive(this.random, -8, 8), Mth.randomBetweenInclusive(this.random, -8, 8), Mth.randomBetweenInclusive(this.random, -8, 8));
if (target.getY() <= this.level().getMinY() || !this.level().isEmptyBlock(target) || !this.level().getWorldBorder().isWithinBounds(target) || !this.level().noCollision(this, new AABB(target).deflate(1.0E-6)) || (attachmentDirection = this.findAttachableSurface(target)) == null) continue;
this.unRide();
this.setAttachFace(attachmentDirection);
this.playSound(SoundEvents.SHULKER_TELEPORT, 1.0f, 1.0f);
this.setPos((double)target.getX() + 0.5, target.getY(), (double)target.getZ() + 0.5);
this.level().gameEvent(GameEvent.TELEPORT, current, GameEvent.Context.of(this));
this.entityData.set(DATA_PEEK_ID, (byte)0);
this.setTarget(null);
return true;
}
return false;
}
@Override
public InterpolationHandler getInterpolation() {
return null;
}
@Override
public boolean hurtServer(ServerLevel level, DamageSource source, float damage) {
Entity directEntity;
if (this.isClosed() && (directEntity = source.getDirectEntity()) instanceof AbstractArrow) {
return false;
}
if (super.hurtServer(level, source, damage)) {
if ((double)this.getHealth() < (double)this.getMaxHealth() * 0.5 && this.random.nextInt(4) == 0) {
this.teleportSomewhere();
} else if (source.is(DamageTypeTags.IS_PROJECTILE) && (directEntity = source.getDirectEntity()) != null && directEntity.getType() == EntityType.SHULKER_BULLET) {
this.hitByShulkerBullet();
}
return true;
}
return false;
}
private boolean isClosed() {
return this.getRawPeekAmount() == 0;
}
private void hitByShulkerBullet() {
Vec3 oldPosition = this.position();
AABB oldAabb = this.getBoundingBox();
if (this.isClosed() || !this.teleportSomewhere()) {
return;
}
int shulkerCount = this.level().getEntities(EntityType.SHULKER, oldAabb.inflate(8.0), Entity::isAlive).size();
float failureChance = (float)(shulkerCount - 1) / 5.0f;
if (this.level().random.nextFloat() < failureChance) {
return;
}
Shulker baby = EntityType.SHULKER.create(this.level(), EntitySpawnReason.BREEDING);
if (baby != null) {
baby.setVariant(this.getVariant());
baby.snapTo(oldPosition);
this.level().addFreshEntity(baby);
}
}
@Override
public boolean canBeCollidedWith(@Nullable Entity other) {
return this.isAlive();
}
public Direction getAttachFace() {
return this.entityData.get(DATA_ATTACH_FACE_ID);
}
private void setAttachFace(Direction attachmentDirection) {
this.entityData.set(DATA_ATTACH_FACE_ID, attachmentDirection);
}
@Override
public void onSyncedDataUpdated(EntityDataAccessor<?> accessor) {
if (DATA_ATTACH_FACE_ID.equals(accessor)) {
this.setBoundingBox(this.makeBoundingBox());
}
super.onSyncedDataUpdated(accessor);
}
private int getRawPeekAmount() {
return this.entityData.get(DATA_PEEK_ID).byteValue();
}
private void setRawPeekAmount(int amount) {
if (!this.level().isClientSide()) {
this.getAttribute(Attributes.ARMOR).removeModifier(COVERED_ARMOR_MODIFIER_ID);
if (amount == 0) {
this.getAttribute(Attributes.ARMOR).addPermanentModifier(COVERED_ARMOR_MODIFIER);
this.playSound(SoundEvents.SHULKER_CLOSE, 1.0f, 1.0f);
this.gameEvent(GameEvent.CONTAINER_CLOSE);
} else {
this.playSound(SoundEvents.SHULKER_OPEN, 1.0f, 1.0f);
this.gameEvent(GameEvent.CONTAINER_OPEN);
}
}
this.entityData.set(DATA_PEEK_ID, (byte)amount);
}
public float getClientPeekAmount(float a) {
return Mth.lerp(a, this.currentPeekAmountO, this.currentPeekAmount);
}
@Override
public void recreateFromPacket(ClientboundAddEntityPacket packet) {
super.recreateFromPacket(packet);
this.yBodyRot = 0.0f;
this.yBodyRotO = 0.0f;
}
@Override
public int getMaxHeadXRot() {
return 180;
}
@Override
public int getMaxHeadYRot() {
return 180;
}
@Override
public void push(Entity entity) {
}
public @Nullable Vec3 getRenderPosition(float a) {
if (this.clientOldAttachPosition == null || this.clientSideTeleportInterpolation <= 0) {
return null;
}
double scale = (double)((float)this.clientSideTeleportInterpolation - a) / 6.0;
scale *= scale;
BlockPos currentPos = this.blockPosition();
double ox = (double)(currentPos.getX() - this.clientOldAttachPosition.getX()) * (scale *= (double)this.getScale());
double oy = (double)(currentPos.getY() - this.clientOldAttachPosition.getY()) * scale;
double oz = (double)(currentPos.getZ() - this.clientOldAttachPosition.getZ()) * scale;
return new Vec3(-ox, -oy, -oz);
}
@Override
protected float sanitizeScale(float scale) {
return Math.min(scale, 3.0f);
}
private void setVariant(Optional<DyeColor> color) {
this.entityData.set(DATA_COLOR_ID, color.map(dyeColor -> (byte)dyeColor.getId()).orElse((byte)16));
}
public Optional<DyeColor> getVariant() {
return Optional.ofNullable(this.getColor());
}
public @Nullable DyeColor getColor() {
byte color = this.entityData.get(DATA_COLOR_ID);
if (color == 16 || color > 15) {
return null;
}
return DyeColor.byId(color);
}
@Override
public <T> @Nullable T get(DataComponentType<? extends T> type) {
if (type == DataComponents.SHULKER_COLOR) {
return Shulker.castComponentValue(type, this.getColor());
}
return super.get(type);
}
@Override
protected void applyImplicitComponents(DataComponentGetter components) {
this.applyImplicitComponentIfPresent(components, DataComponents.SHULKER_COLOR);
super.applyImplicitComponents(components);
}
@Override
protected <T> boolean applyImplicitComponent(DataComponentType<T> type, T value) {
if (type == DataComponents.SHULKER_COLOR) {
this.setVariant(Optional.of(Shulker.castComponentValue(DataComponents.SHULKER_COLOR, value)));
return true;
}
return super.applyImplicitComponent(type, value);
}
private class ShulkerLookControl
extends LookControl {
public ShulkerLookControl(Mob mob) {
super(mob);
}
@Override
protected void clampHeadRotationToBody() {
}
@Override
protected Optional<Float> getYRotD() {
Direction attachFace = Shulker.this.getAttachFace().getOpposite();
Vector3f forward = attachFace.getRotation().transform(new Vector3f((Vector3fc)FORWARD));
Vec3i upNormal = attachFace.getUnitVec3i();
Vector3f right = new Vector3f((float)upNormal.getX(), (float)upNormal.getY(), (float)upNormal.getZ());
right.cross((Vector3fc)forward);
double xd = this.wantedX - this.mob.getX();
double yd = this.wantedY - this.mob.getEyeY();
double zd = this.wantedZ - this.mob.getZ();
Vector3f out = new Vector3f((float)xd, (float)yd, (float)zd);
float deltaRight = right.dot((Vector3fc)out);
float deltaForward = forward.dot((Vector3fc)out);
return Math.abs(deltaRight) > 1.0E-5f || Math.abs(deltaForward) > 1.0E-5f ? Optional.of(Float.valueOf((float)(Mth.atan2(-deltaRight, deltaForward) * 57.2957763671875))) : Optional.empty();
}
@Override
protected Optional<Float> getXRotD() {
return Optional.of(Float.valueOf(0.0f));
}
}
private class ShulkerAttackGoal
extends Goal {
private int attackTime;
public ShulkerAttackGoal() {
this.setFlags(EnumSet.of(Goal.Flag.MOVE, Goal.Flag.LOOK));
}
@Override
public boolean canUse() {
LivingEntity target = Shulker.this.getTarget();
if (target == null || !target.isAlive()) {
return false;
}
return Shulker.this.level().getDifficulty() != Difficulty.PEACEFUL;
}
@Override
public void start() {
this.attackTime = 20;
Shulker.this.setRawPeekAmount(100);
}
@Override
public void stop() {
Shulker.this.setRawPeekAmount(0);
}
@Override
public boolean requiresUpdateEveryTick() {
return true;
}
@Override
public void tick() {
if (Shulker.this.level().getDifficulty() == Difficulty.PEACEFUL) {
return;
}
--this.attackTime;
LivingEntity target = Shulker.this.getTarget();
if (target == null) {
return;
}
Shulker.this.getLookControl().setLookAt(target, 180.0f, 180.0f);
double distance = Shulker.this.distanceToSqr(target);
if (distance < 400.0) {
if (this.attackTime <= 0) {
this.attackTime = 20 + Shulker.this.random.nextInt(10) * 20 / 2;
Shulker.this.level().addFreshEntity(new ShulkerBullet(Shulker.this.level(), Shulker.this, target, Shulker.this.getAttachFace().getAxis()));
Shulker.this.playSound(SoundEvents.SHULKER_SHOOT, 2.0f, (Shulker.this.random.nextFloat() - Shulker.this.random.nextFloat()) * 0.2f + 1.0f);
}
} else {
Shulker.this.setTarget(null);
}
super.tick();
}
}
private class ShulkerPeekGoal
extends Goal {
private int peekTime;
private ShulkerPeekGoal() {
}
@Override
public boolean canUse() {
return Shulker.this.getTarget() == null && Shulker.this.random.nextInt(ShulkerPeekGoal.reducedTickDelay(40)) == 0 && Shulker.this.canStayAt(Shulker.this.blockPosition(), Shulker.this.getAttachFace());
}
@Override
public boolean canContinueToUse() {
return Shulker.this.getTarget() == null && this.peekTime > 0;
}
@Override
public void start() {
this.peekTime = this.adjustedTickDelay(20 * (1 + Shulker.this.random.nextInt(3)));
Shulker.this.setRawPeekAmount(30);
}
@Override
public void stop() {
if (Shulker.this.getTarget() == null) {
Shulker.this.setRawPeekAmount(0);
}
}
@Override
public void tick() {
--this.peekTime;
}
}
private class ShulkerNearestAttackGoal
extends NearestAttackableTargetGoal<Player> {
public ShulkerNearestAttackGoal(Shulker mob) {
super((Mob)mob, Player.class, true);
}
@Override
public boolean canUse() {
if (Shulker.this.level().getDifficulty() == Difficulty.PEACEFUL) {
return false;
}
return super.canUse();
}
@Override
protected AABB getTargetSearchArea(double followDistance) {
Direction attachFace = ((Shulker)this.mob).getAttachFace();
if (attachFace.getAxis() == Direction.Axis.X) {
return this.mob.getBoundingBox().inflate(4.0, followDistance, followDistance);
}
if (attachFace.getAxis() == Direction.Axis.Z) {
return this.mob.getBoundingBox().inflate(followDistance, followDistance, 4.0);
}
return this.mob.getBoundingBox().inflate(followDistance, 4.0, followDistance);
}
}
private static class ShulkerDefenseAttackGoal
extends NearestAttackableTargetGoal<LivingEntity> {
public ShulkerDefenseAttackGoal(Shulker mob) {
super(mob, LivingEntity.class, 10, true, false, (input, level) -> input instanceof Enemy);
}
@Override
public boolean canUse() {
if (this.mob.getTeam() == null) {
return false;
}
return super.canUse();
}
@Override
protected AABB getTargetSearchArea(double followDistance) {
Direction attachFace = ((Shulker)this.mob).getAttachFace();
if (attachFace.getAxis() == Direction.Axis.X) {
return this.mob.getBoundingBox().inflate(4.0, followDistance, followDistance);
}
if (attachFace.getAxis() == Direction.Axis.Z) {
return this.mob.getBoundingBox().inflate(followDistance, followDistance, 4.0);
}
return this.mob.getBoundingBox().inflate(followDistance, 4.0, followDistance);
}
}
private static class ShulkerBodyRotationControl
extends BodyRotationControl {
public ShulkerBodyRotationControl(Mob mob) {
super(mob);
}
@Override
public void clientTick() {
}
}
}