239 lines
		
	
	
	
		
			8.7 KiB
		
	
	
	
		
			Java
		
	
	
	
	
	
			
		
		
	
	
			239 lines
		
	
	
	
		
			8.7 KiB
		
	
	
	
		
			Java
		
	
	
	
	
	
| package net.minecraft.world.entity.monster;
 | |
| 
 | |
| import java.time.LocalDate;
 | |
| import java.time.temporal.ChronoField;
 | |
| import net.minecraft.core.BlockPos;
 | |
| 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;
 | |
| import net.minecraft.world.level.storage.ValueInput;
 | |
| import org.jetbrains.annotations.Nullable;
 | |
| 
 | |
| 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> entityType, Level level) {
 | |
| 		super(entityType, level);
 | |
| 		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 pos, BlockState state) {
 | |
| 		this.playSound(this.getStepSound(), 0.15F, 1.0F);
 | |
| 	}
 | |
| 
 | |
| 	abstract SoundEvent getStepSound();
 | |
| 
 | |
| 	@Override
 | |
| 	public void aiStep() {
 | |
| 		boolean bl = this.isSunBurnTick();
 | |
| 		if (bl) {
 | |
| 			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);
 | |
| 					}
 | |
| 				}
 | |
| 
 | |
| 				bl = false;
 | |
| 			}
 | |
| 
 | |
| 			if (bl) {
 | |
| 				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 random, DifficultyInstance difficulty) {
 | |
| 		super.populateDefaultEquipmentSlots(random, difficulty);
 | |
| 		this.setItemSlot(EquipmentSlot.MAINHAND, new ItemStack(Items.BOW));
 | |
| 	}
 | |
| 
 | |
| 	@Nullable
 | |
| 	@Override
 | |
| 	public SpawnGroupData finalizeSpawn(
 | |
| 		ServerLevelAccessor level, DifficultyInstance difficulty, EntitySpawnReason spawnReason, @Nullable SpawnGroupData spawnGroupData
 | |
| 	) {
 | |
| 		spawnGroupData = super.finalizeSpawn(level, difficulty, spawnReason, spawnGroupData);
 | |
| 		RandomSource randomSource = level.getRandom();
 | |
| 		this.populateDefaultEquipmentSlots(randomSource, difficulty);
 | |
| 		this.populateDefaultEquipmentEnchantments(level, randomSource, difficulty);
 | |
| 		this.reassessWeaponGoal();
 | |
| 		this.setCanPickUpLoot(randomSource.nextFloat() < 0.55F * difficulty.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 spawnGroupData;
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Sets this entity's combat AI.
 | |
| 	 */
 | |
| 	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 target, float velocity) {
 | |
| 		ItemStack itemStack = this.getItemInHand(ProjectileUtil.getWeaponHoldingHand(this, Items.BOW));
 | |
| 		ItemStack itemStack2 = this.getProjectile(itemStack);
 | |
| 		AbstractArrow abstractArrow = this.getArrow(itemStack2, velocity, itemStack);
 | |
| 		double d = target.getX() - this.getX();
 | |
| 		double e = target.getY(0.3333333333333333) - abstractArrow.getY();
 | |
| 		double f = target.getZ() - this.getZ();
 | |
| 		double g = Math.sqrt(d * d + f * f);
 | |
| 		if (this.level() instanceof ServerLevel serverLevel) {
 | |
| 			Projectile.spawnProjectileUsingShoot(abstractArrow, serverLevel, itemStack2, d, e + g * 0.2F, f, 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 arrow, float velocity, @Nullable ItemStack weapon) {
 | |
| 		return ProjectileUtil.getMobArrow(this, arrow, velocity, weapon);
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	public boolean canFireProjectileWeapon(ProjectileWeaponItem projectileWeapon) {
 | |
| 		return projectileWeapon == Items.BOW;
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	public TagKey<Item> getPreferredWeaponType() {
 | |
| 		return ItemTags.SKELETON_PREFERRED_WEAPONS;
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	protected void readAdditionalSaveData(ValueInput input) {
 | |
| 		super.readAdditionalSaveData(input);
 | |
| 		this.reassessWeaponGoal();
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	public void onEquipItem(EquipmentSlot slot, ItemStack oldItem, ItemStack newItem) {
 | |
| 		super.onEquipItem(slot, oldItem, newItem);
 | |
| 		if (!this.level().isClientSide) {
 | |
| 			this.reassessWeaponGoal();
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	public boolean isShaking() {
 | |
| 		return this.isFullyFrozen();
 | |
| 	}
 | |
| }
 |