234 lines
9.7 KiB
Java
234 lines
9.7 KiB
Java
|
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<AbstractSkeleton> 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<? extends AbstractSkeleton> 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<Item> 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();
|
||
|
}
|
||
|
}
|