package net.minecraft.world.entity.monster; import java.time.LocalDate; import java.time.temporal.ChronoField; import javax.annotation.Nullable; import net.minecraft.core.BlockPos; import net.minecraft.nbt.CompoundTag; import net.minecraft.server.level.ServerLevel; import net.minecraft.sounds.SoundEvent; import net.minecraft.sounds.SoundEvents; import net.minecraft.tags.ItemTags; import net.minecraft.tags.TagKey; import net.minecraft.util.RandomSource; import net.minecraft.world.Difficulty; import net.minecraft.world.DifficultyInstance; import net.minecraft.world.entity.EntitySpawnReason; import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.EquipmentSlot; import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.PathfinderMob; import net.minecraft.world.entity.SpawnGroupData; import net.minecraft.world.entity.ai.attributes.AttributeSupplier; import net.minecraft.world.entity.ai.attributes.Attributes; import net.minecraft.world.entity.ai.goal.AvoidEntityGoal; import net.minecraft.world.entity.ai.goal.FleeSunGoal; import net.minecraft.world.entity.ai.goal.LookAtPlayerGoal; import net.minecraft.world.entity.ai.goal.MeleeAttackGoal; import net.minecraft.world.entity.ai.goal.RandomLookAroundGoal; import net.minecraft.world.entity.ai.goal.RangedBowAttackGoal; import net.minecraft.world.entity.ai.goal.RestrictSunGoal; import net.minecraft.world.entity.ai.goal.WaterAvoidingRandomStrollGoal; import net.minecraft.world.entity.ai.goal.target.HurtByTargetGoal; import net.minecraft.world.entity.ai.goal.target.NearestAttackableTargetGoal; import net.minecraft.world.entity.animal.IronGolem; import net.minecraft.world.entity.animal.Turtle; import net.minecraft.world.entity.animal.wolf.Wolf; import net.minecraft.world.entity.player.Player; import net.minecraft.world.entity.projectile.AbstractArrow; import net.minecraft.world.entity.projectile.Projectile; import net.minecraft.world.entity.projectile.ProjectileUtil; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Items; import net.minecraft.world.item.ProjectileWeaponItem; 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; public abstract class AbstractSkeleton extends Monster implements RangedAttackMob { private static final int HARD_ATTACK_INTERVAL = 20; private static final int NORMAL_ATTACK_INTERVAL = 40; private final RangedBowAttackGoal bowGoal = new RangedBowAttackGoal<>(this, 1.0, 20, 15.0F); private final MeleeAttackGoal meleeGoal = new MeleeAttackGoal(this, 1.2, false) { @Override public void stop() { super.stop(); AbstractSkeleton.this.setAggressive(false); } @Override public void start() { super.start(); AbstractSkeleton.this.setAggressive(true); } }; protected AbstractSkeleton(EntityType p_32133_, Level p_32134_) { super(p_32133_, p_32134_); this.reassessWeaponGoal(); } @Override protected void registerGoals() { this.goalSelector.addGoal(2, new RestrictSunGoal(this)); this.goalSelector.addGoal(3, new FleeSunGoal(this, 1.0)); this.goalSelector.addGoal(3, new AvoidEntityGoal<>(this, Wolf.class, 6.0F, 1.0, 1.2)); this.goalSelector.addGoal(5, new WaterAvoidingRandomStrollGoal(this, 1.0)); this.goalSelector.addGoal(6, new LookAtPlayerGoal(this, Player.class, 8.0F)); this.goalSelector.addGoal(6, new RandomLookAroundGoal(this)); this.targetSelector.addGoal(1, new HurtByTargetGoal(this)); this.targetSelector.addGoal(2, new NearestAttackableTargetGoal<>(this, Player.class, true)); this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, IronGolem.class, true)); this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, Turtle.class, 10, true, false, Turtle.BABY_ON_LAND_SELECTOR)); } public static AttributeSupplier.Builder createAttributes() { return Monster.createMonsterAttributes().add(Attributes.MOVEMENT_SPEED, 0.25); } @Override protected void playStepSound(BlockPos p_32159_, BlockState p_32160_) { this.playSound(this.getStepSound(), 0.15F, 1.0F); } abstract SoundEvent getStepSound(); @Override public void aiStep() { boolean flag = this.isSunBurnTick(); if (flag) { ItemStack itemstack = this.getItemBySlot(EquipmentSlot.HEAD); if (!itemstack.isEmpty()) { if (itemstack.isDamageableItem()) { Item item = itemstack.getItem(); itemstack.setDamageValue(itemstack.getDamageValue() + this.random.nextInt(2)); if (itemstack.getDamageValue() >= itemstack.getMaxDamage()) { this.onEquippedItemBroken(item, EquipmentSlot.HEAD); this.setItemSlot(EquipmentSlot.HEAD, ItemStack.EMPTY); } } flag = false; } if (flag) { this.igniteForSeconds(8.0F); } } super.aiStep(); } @Override public void rideTick() { super.rideTick(); if (this.getControlledVehicle() instanceof PathfinderMob pathfindermob) { this.yBodyRot = pathfindermob.yBodyRot; } } @Override protected void populateDefaultEquipmentSlots(RandomSource p_218949_, DifficultyInstance p_218950_) { super.populateDefaultEquipmentSlots(p_218949_, p_218950_); this.setItemSlot(EquipmentSlot.MAINHAND, new ItemStack(Items.BOW)); } @Nullable @Override public SpawnGroupData finalizeSpawn(ServerLevelAccessor p_32146_, DifficultyInstance p_32147_, EntitySpawnReason p_361344_, @Nullable SpawnGroupData p_32149_) { p_32149_ = super.finalizeSpawn(p_32146_, p_32147_, p_361344_, p_32149_); RandomSource randomsource = p_32146_.getRandom(); this.populateDefaultEquipmentSlots(randomsource, p_32147_); this.populateDefaultEquipmentEnchantments(p_32146_, randomsource, p_32147_); this.reassessWeaponGoal(); this.setCanPickUpLoot(randomsource.nextFloat() < 0.55F * p_32147_.getSpecialMultiplier()); if (this.getItemBySlot(EquipmentSlot.HEAD).isEmpty()) { LocalDate localdate = LocalDate.now(); int i = localdate.get(ChronoField.DAY_OF_MONTH); int j = localdate.get(ChronoField.MONTH_OF_YEAR); if (j == 10 && i == 31 && randomsource.nextFloat() < 0.25F) { this.setItemSlot(EquipmentSlot.HEAD, new ItemStack(randomsource.nextFloat() < 0.1F ? Blocks.JACK_O_LANTERN : Blocks.CARVED_PUMPKIN)); this.setDropChance(EquipmentSlot.HEAD, 0.0F); } } return p_32149_; } public void reassessWeaponGoal() { if (this.level() != null && !this.level().isClientSide) { this.goalSelector.removeGoal(this.meleeGoal); this.goalSelector.removeGoal(this.bowGoal); ItemStack itemstack = this.getItemInHand(ProjectileUtil.getWeaponHoldingHand(this, Items.BOW)); if (itemstack.is(Items.BOW)) { int i = this.getHardAttackInterval(); if (this.level().getDifficulty() != Difficulty.HARD) { i = this.getAttackInterval(); } this.bowGoal.setMinAttackInterval(i); this.goalSelector.addGoal(4, this.bowGoal); } else { this.goalSelector.addGoal(4, this.meleeGoal); } } } protected int getHardAttackInterval() { return 20; } protected int getAttackInterval() { return 40; } @Override public void performRangedAttack(LivingEntity p_32141_, float p_32142_) { ItemStack itemstack = this.getItemInHand(ProjectileUtil.getWeaponHoldingHand(this, Items.BOW)); ItemStack itemstack1 = this.getProjectile(itemstack); AbstractArrow abstractarrow = this.getArrow(itemstack1, p_32142_, itemstack); double d0 = p_32141_.getX() - this.getX(); double d1 = p_32141_.getY(0.3333333333333333) - abstractarrow.getY(); double d2 = p_32141_.getZ() - this.getZ(); double d3 = Math.sqrt(d0 * d0 + d2 * d2); if (this.level() instanceof ServerLevel serverlevel) { Projectile.spawnProjectileUsingShoot(abstractarrow, serverlevel, itemstack1, d0, d1 + d3 * 0.2F, d2, 1.6F, 14 - serverlevel.getDifficulty().getId() * 4); } this.playSound(SoundEvents.SKELETON_SHOOT, 1.0F, 1.0F / (this.getRandom().nextFloat() * 0.4F + 0.8F)); } protected AbstractArrow getArrow(ItemStack p_32156_, float p_32157_, @Nullable ItemStack p_343583_) { return ProjectileUtil.getMobArrow(this, p_32156_, p_32157_, p_343583_); } @Override public boolean canFireProjectileWeapon(ProjectileWeaponItem p_32144_) { return p_32144_ == Items.BOW; } @Override public TagKey getPreferredWeaponType() { return ItemTags.SKELETON_PREFERRED_WEAPONS; } @Override public void readAdditionalSaveData(CompoundTag p_32152_) { super.readAdditionalSaveData(p_32152_); this.reassessWeaponGoal(); } @Override public void onEquipItem(EquipmentSlot p_392016_, ItemStack p_392506_, ItemStack p_396799_) { super.onEquipItem(p_392016_, p_392506_, p_396799_); if (!this.level().isClientSide) { this.reassessWeaponGoal(); } } public boolean isShaking() { return this.isFullyFrozen(); } }