237 lines
		
	
	
	
		
			8.1 KiB
		
	
	
	
		
			Java
		
	
	
	
	
	
			
		
		
	
	
			237 lines
		
	
	
	
		
			8.1 KiB
		
	
	
	
		
			Java
		
	
	
	
	
	
| package net.minecraft.world.entity.monster;
 | |
| 
 | |
| import net.minecraft.core.BlockPos;
 | |
| import net.minecraft.core.Holder;
 | |
| import net.minecraft.network.syncher.EntityDataAccessor;
 | |
| import net.minecraft.network.syncher.EntityDataSerializers;
 | |
| import net.minecraft.network.syncher.SynchedEntityData;
 | |
| import net.minecraft.sounds.SoundEvent;
 | |
| import net.minecraft.sounds.SoundEvents;
 | |
| import net.minecraft.util.RandomSource;
 | |
| import net.minecraft.world.Difficulty;
 | |
| import net.minecraft.world.DifficultyInstance;
 | |
| import net.minecraft.world.damagesource.DamageSource;
 | |
| import net.minecraft.world.effect.MobEffect;
 | |
| import net.minecraft.world.effect.MobEffectInstance;
 | |
| import net.minecraft.world.effect.MobEffects;
 | |
| import net.minecraft.world.entity.Entity;
 | |
| import net.minecraft.world.entity.EntitySpawnReason;
 | |
| import net.minecraft.world.entity.EntityType;
 | |
| import net.minecraft.world.entity.LivingEntity;
 | |
| 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.FloatGoal;
 | |
| import net.minecraft.world.entity.ai.goal.LeapAtTargetGoal;
 | |
| 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.WaterAvoidingRandomStrollGoal;
 | |
| import net.minecraft.world.entity.ai.goal.target.HurtByTargetGoal;
 | |
| import net.minecraft.world.entity.ai.goal.target.NearestAttackableTargetGoal;
 | |
| import net.minecraft.world.entity.ai.navigation.PathNavigation;
 | |
| import net.minecraft.world.entity.ai.navigation.WallClimberNavigation;
 | |
| import net.minecraft.world.entity.animal.IronGolem;
 | |
| import net.minecraft.world.entity.animal.armadillo.Armadillo;
 | |
| import net.minecraft.world.entity.player.Player;
 | |
| 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.phys.Vec3;
 | |
| import org.jetbrains.annotations.Nullable;
 | |
| 
 | |
| public class Spider extends Monster {
 | |
| 	private static final EntityDataAccessor<Byte> DATA_FLAGS_ID = SynchedEntityData.defineId(Spider.class, EntityDataSerializers.BYTE);
 | |
| 	private static final float SPIDER_SPECIAL_EFFECT_CHANCE = 0.1F;
 | |
| 
 | |
| 	public Spider(EntityType<? extends Spider> entityType, Level level) {
 | |
| 		super(entityType, level);
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	protected void registerGoals() {
 | |
| 		this.goalSelector.addGoal(1, new FloatGoal(this));
 | |
| 		this.goalSelector.addGoal(2, new AvoidEntityGoal(this, Armadillo.class, 6.0F, 1.0, 1.2, livingEntity -> !((Armadillo)livingEntity).isScared()));
 | |
| 		this.goalSelector.addGoal(3, new LeapAtTargetGoal(this, 0.4F));
 | |
| 		this.goalSelector.addGoal(4, new Spider.SpiderAttackGoal(this));
 | |
| 		this.goalSelector.addGoal(5, new WaterAvoidingRandomStrollGoal(this, 0.8));
 | |
| 		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 Spider.SpiderTargetGoal(this, Player.class));
 | |
| 		this.targetSelector.addGoal(3, new Spider.SpiderTargetGoal(this, IronGolem.class));
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	protected PathNavigation createNavigation(Level level) {
 | |
| 		return new WallClimberNavigation(this, level);
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	protected void defineSynchedData(SynchedEntityData.Builder builder) {
 | |
| 		super.defineSynchedData(builder);
 | |
| 		builder.define(DATA_FLAGS_ID, (byte)0);
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	public void tick() {
 | |
| 		super.tick();
 | |
| 		if (!this.level().isClientSide) {
 | |
| 			this.setClimbing(this.horizontalCollision);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	public static AttributeSupplier.Builder createAttributes() {
 | |
| 		return Monster.createMonsterAttributes().add(Attributes.MAX_HEALTH, 16.0).add(Attributes.MOVEMENT_SPEED, 0.3F);
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	protected SoundEvent getAmbientSound() {
 | |
| 		return SoundEvents.SPIDER_AMBIENT;
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	protected SoundEvent getHurtSound(DamageSource damageSource) {
 | |
| 		return SoundEvents.SPIDER_HURT;
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	protected SoundEvent getDeathSound() {
 | |
| 		return SoundEvents.SPIDER_DEATH;
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	protected void playStepSound(BlockPos pos, BlockState state) {
 | |
| 		this.playSound(SoundEvents.SPIDER_STEP, 0.15F, 1.0F);
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	public boolean onClimbable() {
 | |
| 		return this.isClimbing();
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	public void makeStuckInBlock(BlockState state, Vec3 motionMultiplier) {
 | |
| 		if (!state.is(Blocks.COBWEB)) {
 | |
| 			super.makeStuckInBlock(state, motionMultiplier);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	public boolean canBeAffected(MobEffectInstance effectInstance) {
 | |
| 		return effectInstance.is(MobEffects.POISON) ? false : super.canBeAffected(effectInstance);
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Returns {@code true} if the WatchableObject (Byte) is 0x01 otherwise returns {@code false}. The WatchableObject is updated using setBesideClimbableBlock.
 | |
| 	 */
 | |
| 	public boolean isClimbing() {
 | |
| 		return (this.entityData.get(DATA_FLAGS_ID) & 1) != 0;
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Updates the WatchableObject (Byte) created in entityInit(), setting it to 0x01 if par1 is true or 0x00 if it is false.
 | |
| 	 */
 | |
| 	public void setClimbing(boolean climbing) {
 | |
| 		byte b = this.entityData.get(DATA_FLAGS_ID);
 | |
| 		if (climbing) {
 | |
| 			b = (byte)(b | 1);
 | |
| 		} else {
 | |
| 			b = (byte)(b & -2);
 | |
| 		}
 | |
| 
 | |
| 		this.entityData.set(DATA_FLAGS_ID, b);
 | |
| 	}
 | |
| 
 | |
| 	@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();
 | |
| 		if (randomSource.nextInt(100) == 0) {
 | |
| 			Skeleton skeleton = EntityType.SKELETON.create(this.level(), EntitySpawnReason.JOCKEY);
 | |
| 			if (skeleton != null) {
 | |
| 				skeleton.snapTo(this.getX(), this.getY(), this.getZ(), this.getYRot(), 0.0F);
 | |
| 				skeleton.finalizeSpawn(level, difficulty, spawnReason, null);
 | |
| 				skeleton.startRiding(this);
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		if (spawnGroupData == null) {
 | |
| 			spawnGroupData = new Spider.SpiderEffectsGroupData();
 | |
| 			if (level.getDifficulty() == Difficulty.HARD && randomSource.nextFloat() < 0.1F * difficulty.getSpecialMultiplier()) {
 | |
| 				((Spider.SpiderEffectsGroupData)spawnGroupData).setRandomEffect(randomSource);
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		if (spawnGroupData instanceof Spider.SpiderEffectsGroupData spiderEffectsGroupData) {
 | |
| 			Holder<MobEffect> holder = spiderEffectsGroupData.effect;
 | |
| 			if (holder != null) {
 | |
| 				this.addEffect(new MobEffectInstance(holder, -1));
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		return spawnGroupData;
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	public Vec3 getVehicleAttachmentPoint(Entity entity) {
 | |
| 		return entity.getBbWidth() <= this.getBbWidth() ? new Vec3(0.0, 0.3125 * this.getScale(), 0.0) : super.getVehicleAttachmentPoint(entity);
 | |
| 	}
 | |
| 
 | |
| 	static class SpiderAttackGoal extends MeleeAttackGoal {
 | |
| 		public SpiderAttackGoal(Spider spider) {
 | |
| 			super(spider, 1.0, true);
 | |
| 		}
 | |
| 
 | |
| 		@Override
 | |
| 		public boolean canUse() {
 | |
| 			return super.canUse() && !this.mob.isVehicle();
 | |
| 		}
 | |
| 
 | |
| 		@Override
 | |
| 		public boolean canContinueToUse() {
 | |
| 			float f = this.mob.getLightLevelDependentMagicValue();
 | |
| 			if (f >= 0.5F && this.mob.getRandom().nextInt(100) == 0) {
 | |
| 				this.mob.setTarget(null);
 | |
| 				return false;
 | |
| 			} else {
 | |
| 				return super.canContinueToUse();
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	public static class SpiderEffectsGroupData implements SpawnGroupData {
 | |
| 		@Nullable
 | |
| 		public Holder<MobEffect> effect;
 | |
| 
 | |
| 		public void setRandomEffect(RandomSource random) {
 | |
| 			int i = random.nextInt(5);
 | |
| 			if (i <= 1) {
 | |
| 				this.effect = MobEffects.SPEED;
 | |
| 			} else if (i <= 2) {
 | |
| 				this.effect = MobEffects.STRENGTH;
 | |
| 			} else if (i <= 3) {
 | |
| 				this.effect = MobEffects.REGENERATION;
 | |
| 			} else if (i <= 4) {
 | |
| 				this.effect = MobEffects.INVISIBILITY;
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	static class SpiderTargetGoal<T extends LivingEntity> extends NearestAttackableTargetGoal<T> {
 | |
| 		public SpiderTargetGoal(Spider spider, Class<T> entityTypeToTarget) {
 | |
| 			super(spider, entityTypeToTarget, true);
 | |
| 		}
 | |
| 
 | |
| 		@Override
 | |
| 		public boolean canUse() {
 | |
| 			float f = this.mob.getLightLevelDependentMagicValue();
 | |
| 			return f >= 0.5F ? false : super.canUse();
 | |
| 		}
 | |
| 	}
 | |
| }
 |