Source EndermanInfo - Spiderman31807/Mob_Selector_Mod GitHub Wiki

package playasmob;

import net.minecraft.world.phys.Vec3;
import net.minecraft.world.phys.HitResult;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.level.gameevent.GameEvent;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.SoundType;
import net.minecraft.world.level.biome.Biomes;
import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.ClipContext;
import net.minecraft.world.item.context.BlockPlaceContext;
import net.minecraft.world.item.alchemy.Potions;
import net.minecraft.world.item.alchemy.PotionContents;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.entity.projectile.ThrownPotion;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.entity.monster.EnderMan;
import net.minecraft.world.entity.ai.attributes.Attributes;
import net.minecraft.world.entity.NeutralMob;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.EntityDimensions;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.damagesource.DamageSource;
import net.minecraft.util.valueproviders.UniformInt;
import net.minecraft.util.TimeUtil;
import net.minecraft.tags.FluidTags;
import net.minecraft.tags.DamageTypeTags;
import net.minecraft.tags.BlockTags;
import net.minecraft.sounds.SoundSource;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.sounds.SoundEvent;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.resources.ResourceKey;
import net.minecraft.network.syncher.EntityDataAccessor;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.core.particles.ParticleTypes;
import net.minecraft.core.component.DataComponents;
import net.minecraft.core.Direction;
import net.minecraft.core.BlockPos;
import net.minecraft.client.renderer.entity.state.EntityRenderState;
import net.minecraft.client.renderer.entity.state.EndermanRenderState;
import net.minecraft.client.renderer.entity.EntityRenderer;
import net.minecraft.client.renderer.entity.EndermanRenderer;
import net.minecraft.client.gui.GuiGraphics;

import java.util.UUID;
import java.util.List;
import net.minecraft.client.Minecraft;

public class EndermanInfo extends MonsterInfo implements NeutralMob {
	public static final Synched carryState = Synched.CarryState;
	public static final Synched creepy = Synched.Creepy;
	public static final Synched staredAt = Synched.StaredAt;
	public static final UniformInt angerTime = TimeUtil.rangeOfSeconds(20, 39);
	public int lastStareSound = Integer.MIN_VALUE;
	public int targetChangeTime;
	public int remainingPersistentAngerTime;
	public UUID persistentAngerTarget;

	public EndermanInfo(Player player, CompoundTag compound) {
		super(EntityType.ENDERMAN, EnderMan.class, player, compound);
	}

	public EndermanInfo(Player player) {
		this(player, null);
	}

	public EndermanInfo() {
		this(null);
	}

	// Custom Code
	@Override
	public boolean canAttack(LivingEntity target) {
		return super.canAttack(target);
	}

	@Override
	public void setLastHurtByPlayer(Player player) {
		super.setLastHurtByPlayer(player);
	}

	@Override
	public void setLastHurtByMob(LivingEntity entity) {
		super.setLastHurtByMob(entity);
	}

	@Override
	public LivingEntity getLastHurtByMob() {
		return super.getLastHurtByMob();
	}

	@Override
	public void reset() {
		super.reset();
		this.reset(creepy);
		this.reset(staredAt);
		this.lastStareSound = Integer.MIN_VALUE;
		this.targetChangeTime = 0;
		this.remainingPersistentAngerTime = 0;
		this.persistentAngerTarget = null;
	}

	@Override
	public EntityRenderer getRenderer() {
		return new EndermanRenderer(Utils.getContext());
	}

	@Override
	public EntityRenderState createState() {
		return new EndermanRenderState();
	}

	@Override
	public void modifyState(EntityRenderState state) {
		super.modifyState(state);
		if (state instanceof EndermanRenderState endermanState) {
			endermanState.isCreepy = this.isCreepy();
			if (!this.isVisuallySwimming())
				endermanState.carriedBlock = this.getCarriedBlock();
		}
	}

	@Override
	public ResourceKey<Level> getRespawnDimension() {
		return switch (this.random.nextInt(0, 3)) {
			default -> super.getRespawnDimension();
			case 0 -> Level.OVERWORLD;
			case 1 -> Level.NETHER;
			case 2 -> Level.END;
		};
	}

	@Override
	public List<ResourceKey<Biome>> getRespawnBiomes(ResourceKey<Level> dimension) {
		if (dimension.equals(Level.NETHER))
			return List.of(Biomes.NETHER_WASTES, Biomes.SOUL_SAND_VALLEY, Biomes.WARPED_FOREST);
		return super.getRespawnBiomes(dimension);
	}

	@Override
	public float getSpeed(float original) {
		if (this.hasTarget())
			original += 0.15;
		return super.getSpeed(original);
	}

	@Override
	public boolean canJump() {
		return false;
	}

	@Override
	public void press(int key, boolean pressed, int time) {
		if(this.isSpectator())
			return;
	
		if (key == 1 && pressed) {
			if (this.findTarget() instanceof LivingEntity target) {
				this.tryTeleport(target);
			} else {
				this.teleportForward();
			}
		}
		if (key == 2 && pressed)
			this.tryTeleport(null);
		if (key == 3 && pressed) {
			BlockHitResult result = this.blockHit(this.getAttributeBaseValue(Attributes.BLOCK_INTERACTION_RANGE));
			if (result.getType() == HitResult.Type.BLOCK) {
				if (this.getCarriedBlock() instanceof BlockState state) {
					this.placeCarriedBlock(result);
				} else {
					this.pickupBlock(result);
				}
			}
		}
	}

	@Override
	public boolean handUseageBlocked() {
		return this.getCarriedBlock() != null;
	}

	@Override
	public List<HudIcon> getIcons(float partialTick) {
		if (this.isSpectator())
			return super.getIcons(partialTick);
		HudIcon teleport = new HudIcon("textures/item/ender_pearl.png").key(Keybinds.Key1).useable(this.canTeleport(false));
		HudIcon randomTeleport = new HudIcon("textures/item/chorus_fruit.png").key(Keybinds.Key2).useable(this.canTeleport(true));
		List<HudIcon> endermanIcons = List.of(teleport, randomTeleport.offsetX(teleport, true));
		if (this.getCarriedOrTargetBlock() instanceof BlockState state && state.is(BlockTags.ENDERMAN_HOLDABLE)) {
			boolean holdingState = this.getCarriedBlock() instanceof BlockState;
			HudIcon block = new HudIcon() {
				@Override
				public void renderIcon(GuiGraphics graphics, int x, int y) {
					if (state instanceof BlockState blockState)
						graphics.renderFakeItem(new ItemStack(blockState.getBlock().asItem()), x, y);
				}
			};
			block.key(Keybinds.Key3).using(holdingState);
			endermanIcons = Utils.merge(endermanIcons, List.of(block.offsetX(randomTeleport, true)));
		}
		return Utils.merge(super.getIcons(partialTick), endermanIcons);
	}

	public boolean canTeleport(boolean isRandom) {
		return this.isAlive() && (this.isCreative() || this.getFoodData().getFoodLevel() >= (isRandom ? 7 : 10));
	}

	public boolean teleportForward() {
		BlockHitResult result = this.level().clip(new ClipContext(this.player.getEyePosition(1f), this.player.getEyePosition(1f).add(this.player.getViewVector(1f).scale(32)), ClipContext.Block.OUTLINE, ClipContext.Fluid.NONE, this.player));
		EntityDimensions hitbox = this.getDimensions(this.getPose());
		Vec3 location = result.getLocation();
		Vec3 direction = this.player.getEyePosition(1f).subtract(location).normalize();
		Vec3 target = location.add(direction.scale(hitbox.width()));
		result = this.level().clip(new ClipContext(target, target.subtract(0, hitbox.height() * 1.5, 0), ClipContext.Block.OUTLINE, ClipContext.Fluid.NONE, this.player));
		if (result.getType() != HitResult.Type.MISS)
			target = result.getLocation();
		return this.teleport(target.x, target.y, target.z, false);
	}

	public boolean teleportTowards(Vec3 direction) {
		direction.normalize();
		double x = this.getX() + (this.random.nextDouble() - 0.5) * 8 - direction.x * 16;
		double y = this.getY() + (double) (this.random.nextInt(16) - 8) - direction.y * 16;
		double z = this.getZ() + (this.random.nextDouble() - 0.5) * 8 - direction.z * 16;
		return this.teleport(x, y, z, true);
	}

	public boolean tryTeleport(Entity target) {
		if (!this.canTeleport(true))
			return false;

		for (int attempt = 0; attempt < 64; attempt++) {
			if (target == null && this.teleport())
				return true;
			if (target != null && this.teleportTowards(target))
				return true;
		}
		return false;
	}

	public BlockState getCarriedOrTargetBlock() {
		if (this.getCarriedBlock() instanceof BlockState state)
			return state;
		BlockHitResult result = this.blockHit(this.getAttributeBaseValue(Attributes.BLOCK_INTERACTION_RANGE));
		if (result.getType() == HitResult.Type.BLOCK)
			return this.level().getBlockState(result.getBlockPos());
		return null;
	}

	public boolean pickupBlock(BlockHitResult result) {
		BlockPos pos = result.getBlockPos();
		BlockState state = this.level().getBlockState(pos);
		if (!state.is(BlockTags.ENDERMAN_HOLDABLE))
			return false;
			
		SoundType soundtype = state.getSoundType(this.level(), pos, this.player);
		this.level().playSound(player, pos, state.getSoundType(this.level(), pos, this.player).getBreakSound(), SoundSource.BLOCKS, (soundtype.getVolume() + 1) / 2, soundtype.getPitch() * 0.8f);
		if(this.isClient())
			return true;
			
		this.level().removeBlock(pos, false);
		this.level().gameEvent(GameEvent.BLOCK_DESTROY, pos, GameEvent.Context.of(this.player, state));
		this.setCarriedBlock(state.getBlock().defaultBlockState());
		return true;
	}

	public boolean placeCarriedBlock(BlockHitResult result) {
		BlockState newState = this.getCarriedBlock();
		BlockPlaceContext context = new BlockPlaceContext(this.player, null, ItemStack.EMPTY, result);
		if (newState == null || !newState.canSurvive(this.level(), context.getClickedPos()) || !newState.getBlock().isEnabled(this.level().enabledFeatures()) || !context.canPlace())
			return false;
		if (this.isServer() && !this.level().setBlock(context.getClickedPos(), newState, 11))
			return false;
		BlockPos pos = context.getClickedPos();
		BlockState oldState = this.level().getBlockState(pos);
		SoundType soundtype = oldState.getSoundType(this.level(), pos, this.player);
		this.level().playSound(player, pos, newState.getSoundType(this.level(), pos, this.player).getPlaceSound(), SoundSource.BLOCKS, (soundtype.getVolume() + 1) / 2, soundtype.getPitch() * 0.8f);
		if (this.isClient())
			return true;
		this.level().gameEvent(GameEvent.BLOCK_PLACE, pos, GameEvent.Context.of(this.player, oldState));
		this.setCarriedBlock(null);
		return true;
	}

	@Override
	public Diet getDiet() {
		return new Diet();
	}

	// EnderMan.class Code
	@Override
	public void setTarget(LivingEntity target) {
		if (target == null) {
			this.targetChangeTime = 0;
			this.set(creepy, false);
			this.set(staredAt, false);
		} else {
			this.targetChangeTime = this.tickCount();
			this.set(creepy, true);
		}
		super.setTarget(target);
	}

	@Override
	public void startPersistentAngerTimer() {
		this.setRemainingPersistentAngerTime(angerTime.sample(this.random));
	}

	@Override
	public void setRemainingPersistentAngerTime(int time) {
		this.remainingPersistentAngerTime = time;
	}

	@Override
	public int getRemainingPersistentAngerTime() {
		return this.remainingPersistentAngerTime;
	}

	@Override
	public void setPersistentAngerTarget(UUID uuid) {
		this.persistentAngerTarget = uuid;
	}

	@Override
	public UUID getPersistentAngerTarget() {
		return this.persistentAngerTarget;
	}

	public void playStareSound() {
		if (this.tickCount() >= this.lastStareSound + 400) {
			this.lastStareSound = this.tickCount();
			if (!this.isSilent())
				this.level().playLocalSound(this.getX(), this.getEyeY(), this.getZ(), SoundEvents.ENDERMAN_STARE, this.getSoundSource(), 2.5f, 1, false);
		}
	}

	@Override
	public boolean onSyncedDataUpdated(Object object, EntityDataAccessor<?> data) {
		if (creepy.data().equals(data) && this.hasBeenStaredAt() && this.level().isClientSide)
			this.playStareSound();
		return super.onSyncedDataUpdated(null, data);
	}

	@Override
	public CompoundTag saveData() {
		CompoundTag compound = super.saveData();
		CompoundTag angerData = new CompoundTag();
		this.addPersistentAngerSaveData(angerData);
		compound.put("angerData", angerData);
		return compound;
	}

	@Override
	public void loadData(CompoundTag compound) {
		super.loadData(compound);
		if (compound.contains("angerData"))
			this.readPersistentAngerSaveData(this.level(), compound.getCompound("angerData"));
	}

	public boolean isBeingStaredBy(Player player) {
		return !LivingEntity.PLAYER_NOT_WEARING_DISGUISE_ITEM_FOR_TARGET.test(player, this.player) ? false : this.isLookingAtMe(player, 0.025, true, false, new double[]{this.getEyeY()});
	}

	@Override
	public boolean aiStep(Object object) {
		if (this.isClient() && (!this.isSpectator() || Minecraft.getInstance().player instanceof Player client && client.isSpectator())) {
			for (int i = 0; i < 2; i++) {
				this.level().addParticle(ParticleTypes.PORTAL, this.getRandomX(0.5), this.getRandomY() - 0.25, this.getRandomZ(0.5), (this.random.nextDouble() - 0.5) * 2.0, -this.random.nextDouble(), (this.random.nextDouble() - 0.5) * 2.0);
			}
		}
		if (this.level() instanceof ServerLevel server)
			this.updatePersistentAnger(server, true);
		return super.aiStep(null);
	}

	@Override
	public boolean isSensitiveToWater(boolean original) {
		return true;
	}

	@Override
	public void customServerAiStep(ServerLevel server) {
		super.customServerAiStep(server);
		if (this.tickCount() % 60 != 0)
			return;
		float light = this.getLightLevelDependentMagicValue();
		if (server.isDay() && light > 0.5f && server.canSeeSky(this.blockPosition()) && this.random.nextFloat() * 30 < (light - 0.4f) * 2) {
			this.getFoodData().eat(FoodFactory.createFoodData(2, 0f));
		} else {
			this.getFoodData().eat(FoodFactory.createFoodData(1, 0f));
		}
	}

	public boolean teleport() {
		if (this.isServer() && this.isAlive()) {
			double x = this.getX() + (this.random.nextDouble() - 0.5) * 64.0;
			double y = this.getY() + (double) (this.random.nextInt(64) - 32);
			double z = this.getZ() + (this.random.nextDouble() - 0.5) * 64.0;
			return this.teleport(x, y, z, true);
		} else {
			return false;
		}
	}

	public boolean teleportTowards(Entity target) {
		return this.teleportTowards(new Vec3(this.getX() - target.getX(), this.getY(0.5) - target.getEyeY(), this.getZ() - target.getZ()));
	}

	public boolean teleport(double x, double y, double z, boolean random) {
		if (!this.canTeleport(random) || this.isClient())
			return false;

		BlockPos.MutableBlockPos location = new BlockPos.MutableBlockPos(x, y, z);
		while (location.getY() > this.level().getMinY() && !this.level().getBlockState(location).blocksMotion()) {
			location.move(Direction.DOWN);
		}
		BlockState state = this.level().getBlockState(location);
		if (state.blocksMotion() && !state.getFluidState().is(FluidTags.WATER)) {
			net.neoforged.neoforge.event.entity.EntityTeleportEvent.EnderEntity event = net.neoforged.neoforge.event.EventHooks.onEnderTeleport(this.player, x, y, z);
			if (event.isCanceled())
				return false;
			Vec3 originLocation = this.position();
			boolean success = random ? this.randomTeleport(event.getTargetX(), event.getTargetY(), event.getTargetZ(), true) : true;
			if (!random)
				this.teleportTo(event.getTargetX(), event.getTargetY(), event.getTargetZ());
			if (success) {
				this.resetFallDistance();
				this.level().gameEvent(GameEvent.TELEPORT, originLocation, GameEvent.Context.of(this.player));
				if (!this.isCreative())
					this.getFoodData().eat(FoodFactory.createFoodData(random ? -1 : -2, 0));
				if (!this.isSilent()) {
					this.level().playSound(null, this.player.xo, this.player.yo, this.player.zo, SoundEvents.ENDERMAN_TELEPORT, this.getSoundSource(), 1, 1);
					this.playSound(SoundEvents.ENDERMAN_TELEPORT, 1, 1);
				}
			}
			return success;
		} else {
			return false;
		}
	}

	@Override
	public SoundEvent getAmbientSound() {
		return this.isCreepy() ? SoundEvents.ENDERMAN_SCREAM : SoundEvents.ENDERMAN_AMBIENT;
	}

	@Override
	public SoundEvent getHurtSound(SoundEvent original, DamageSource source) {
		return SoundEvents.ENDERMAN_HURT;
	}

	@Override
	public SoundEvent getDeathSound(SoundEvent original) {
		return SoundEvents.ENDERMAN_DEATH;
	}

	public void setCarriedBlock(BlockState state) {
		this.set(carryState, state);
	}

	public BlockState getCarriedBlock() {
		return this.get(carryState);
	}

	@Override
	public boolean hurtServer(boolean original, ServerLevel server, DamageSource source, float damage) {
		if (this.isInvulnerableTo(server, source))
			return false;
		boolean isPotion = source.getDirectEntity() instanceof ThrownPotion;
		if (!source.is(DamageTypeTags.IS_PROJECTILE) && !isPotion) {
			boolean shouldHurt = super.hurtServer(original, server, source, damage);
			/*if (!(source.getEntity() instanceof LivingEntity) && this.random.nextInt(10) != 0)
				this.teleport();*/
			return shouldHurt;
		}
		boolean hurtWater = isPotion && source.getDirectEntity() instanceof ThrownPotion potion && this.hurtWithCleanWater(server, source, potion, damage);
		if (this.tryTeleport(null))
			return true;
		return hurtWater;
	}

	public boolean hurtWithCleanWater(ServerLevel server, DamageSource source, ThrownPotion potion, float damage) {
		PotionContents content = potion.getItem().getOrDefault(DataComponents.POTION_CONTENTS, PotionContents.EMPTY);
		return content.is(Potions.WATER) ? super.hurtServer(server, source, damage) : false;
	}

	public boolean isCreepy() {
		return this.get(creepy);
	}

	public boolean hasBeenStaredAt() {
		return this.get(staredAt);
	}

	public void setBeingStaredAt() {
		this.set(staredAt, true);
	}
}
⚠️ **GitHub.com Fallback** ⚠️