--- a/net/minecraft/world/entity/monster/EnderMan.java
+++ b/net/minecraft/world/entity/monster/EnderMan.java
@@ -116,9 +_,20 @@
             .add(Attributes.STEP_HEIGHT, 1.0);
     }
 
+    // Paper start - EndermanEscapeEvent
+    private boolean tryEscape(com.destroystokyo.paper.event.entity.EndermanEscapeEvent.Reason reason) {
+        return new com.destroystokyo.paper.event.entity.EndermanEscapeEvent((org.bukkit.craftbukkit.entity.CraftEnderman) this.getBukkitEntity(), reason).callEvent();
+    }
+    // Paper end - EndermanEscapeEvent
+
     @Override
-    public void setTarget(@Nullable LivingEntity livingEntity) {
-        super.setTarget(livingEntity);
+    // CraftBukkit start - fire event
+    public boolean setTarget(@Nullable LivingEntity livingEntity, @Nullable org.bukkit.event.entity.EntityTargetEvent.TargetReason reason) {
+        if (!super.setTarget(livingEntity, reason)) {
+            return false;
+        }
+        livingEntity = this.getTarget();
+        // CraftBukkit end
         AttributeInstance attribute = this.getAttribute(Attributes.MOVEMENT_SPEED);
         if (livingEntity == null) {
             this.targetChangeTime = 0;
@@ -132,6 +_,7 @@
                 attribute.addTransientModifier(SPEED_MODIFIER_ATTACKING);
             }
         }
+        return true; // CraftBukkit
     }
 
     @Override
@@ -207,6 +_,15 @@
     }
 
     boolean isBeingStaredBy(Player player) {
+        // Paper start - EndermanAttackPlayerEvent
+        final boolean shouldAttack = this.isBeingStaredBy0(player);
+        final com.destroystokyo.paper.event.entity.EndermanAttackPlayerEvent event = new com.destroystokyo.paper.event.entity.EndermanAttackPlayerEvent((org.bukkit.entity.Enderman) getBukkitEntity(), (org.bukkit.entity.Player) player.getBukkitEntity());
+        event.setCancelled(!shouldAttack);
+        return event.callEvent();
+    }
+
+    boolean isBeingStaredBy0(Player player) {
+        // Paper end - EndermanAttackPlayerEvent
         return LivingEntity.PLAYER_NOT_WEARING_DISGUISE_ITEM.test(player) && this.isLookingAtMe(player, 0.025, true, false, new double[]{this.getEyeY()});
     }
 
@@ -246,7 +_,7 @@
             float lightLevelDependentMagicValue = this.getLightLevelDependentMagicValue();
             if (lightLevelDependentMagicValue > 0.5F
                 && level.canSeeSky(this.blockPosition())
-                && this.random.nextFloat() * 30.0F < (lightLevelDependentMagicValue - 0.4F) * 2.0F) {
+                && this.random.nextFloat() * 30.0F < (lightLevelDependentMagicValue - 0.4F) * 2.0F && this.tryEscape(com.destroystokyo.paper.event.entity.EndermanEscapeEvent.Reason.RUNAWAY)) { // Paper - EndermanEscapeEvent
                 this.setTarget(null);
                 this.teleport();
             }
@@ -255,7 +_,7 @@
         super.customServerAiStep(level);
     }
 
-    protected boolean teleport() {
+    public boolean teleport() {
         if (!this.level().isClientSide() && this.isAlive()) {
             double d = this.getX() + (this.random.nextDouble() - 0.5) * 64.0;
             double d1 = this.getY() + (this.random.nextInt(64) - 32);
@@ -266,7 +_,7 @@
         }
     }
 
-    boolean teleportTowards(Entity target) {
+    public boolean teleportTowards(Entity target) {
         Vec3 vec3 = new Vec3(this.getX() - target.getX(), this.getY(0.5) - target.getEyeY(), this.getZ() - target.getZ());
         vec3 = vec3.normalize();
         double d = 16.0;
@@ -304,17 +_,17 @@
     }
 
     @Override
-    protected SoundEvent getAmbientSound() {
+    public SoundEvent getAmbientSound() {
         return this.isCreepy() ? SoundEvents.ENDERMAN_SCREAM : SoundEvents.ENDERMAN_AMBIENT;
     }
 
     @Override
-    protected SoundEvent getHurtSound(DamageSource damageSource) {
+    public SoundEvent getHurtSound(DamageSource damageSource) {
         return SoundEvents.ENDERMAN_HURT;
     }
 
     @Override
-    protected SoundEvent getDeathSound() {
+    public SoundEvent getDeathSound() {
         return SoundEvents.ENDERMAN_DEATH;
     }
 
@@ -359,21 +_,25 @@
             AbstractThrownPotion abstractThrownPotion1 = damageSource.getDirectEntity() instanceof AbstractThrownPotion abstractThrownPotion
                 ? abstractThrownPotion
                 : null;
-            if (!damageSource.is(DamageTypeTags.IS_PROJECTILE) && abstractThrownPotion1 == null) {
+            if (!damageSource.is(DamageTypeTags.IS_PROJECTILE) && abstractThrownPotion1 == null) { // Paper - EndermanEscapeEvent - diff on change - below logic relies on this path covering non-projectile damage.
                 boolean flag = super.hurtServer(level, damageSource, amount);
                 if (!(damageSource.getEntity() instanceof LivingEntity) && this.random.nextInt(10) != 0) {
+                    if (this.tryEscape(damageSource.is(net.minecraft.tags.DamageTypeTags.IS_DROWNING) ? com.destroystokyo.paper.event.entity.EndermanEscapeEvent.Reason.DROWN : com.destroystokyo.paper.event.entity.EndermanEscapeEvent.Reason.CRITICAL_HIT)) { // Paper - EndermanEscapeEvent
                     this.teleport();
+                    } // Paper - EndermanEscapeEvent
                 }
 
                 return flag;
             } else {
                 boolean flag = abstractThrownPotion1 != null && this.hurtWithCleanWater(level, damageSource, abstractThrownPotion1, amount);
 
+                if (this.tryEscape(com.destroystokyo.paper.event.entity.EndermanEscapeEvent.Reason.INDIRECT)) { // Paper - EndermanEscapeEvent
                 for (int i = 0; i < 64; i++) {
                     if (this.teleport()) {
                         return true;
                     }
                 }
+                } // Paper - EndermanEscapeEvent
 
                 return flag;
             }
@@ -398,6 +_,16 @@
         this.entityData.set(DATA_STARED_AT, true);
     }
 
+    // Paper start
+    public void setCreepy(boolean creepy) {
+        this.entityData.set(DATA_CREEPY, creepy);
+    }
+
+    public void setHasBeenStaredAt(boolean hasBeenStaredAt) {
+        this.entityData.set(DATA_STARED_AT, hasBeenStaredAt);
+    }
+    // Paper end
+
     @Override
     public boolean requiresCustomPersistence() {
         return super.requiresCustomPersistence() || this.getCarriedBlock() != null;
@@ -457,16 +_,19 @@
             int floor1 = Mth.floor(this.enderman.getY() + random.nextDouble() * 2.0);
             int floor2 = Mth.floor(this.enderman.getZ() - 1.0 + random.nextDouble() * 2.0);
             BlockPos blockPos = new BlockPos(floor, floor1, floor2);
-            BlockState blockState = level.getBlockState(blockPos);
+            BlockState blockState = level.getBlockStateIfLoaded(blockPos); // Paper - Prevent endermen from loading chunks
+            if (blockState == null) return; // Paper - Prevent endermen from loading chunks
             BlockPos blockPos1 = blockPos.below();
             BlockState blockState1 = level.getBlockState(blockPos1);
             BlockState carriedBlock = this.enderman.getCarriedBlock();
             if (carriedBlock != null) {
                 carriedBlock = Block.updateFromNeighbourShapes(carriedBlock, this.enderman.level(), blockPos);
                 if (this.canPlaceBlock(level, blockPos, carriedBlock, blockState, blockState1, blockPos1)) {
+                    if (org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(this.enderman, blockPos, carriedBlock)) { // CraftBukkit - Place event
                     level.setBlock(blockPos, carriedBlock, 3);
                     level.gameEvent(GameEvent.BLOCK_PLACE, blockPos, GameEvent.Context.of(this.enderman, carriedBlock));
                     this.enderman.setCarriedBlock(null);
+                    } // CraftBukkit
                 }
             }
         }
@@ -564,7 +_,7 @@
             } else {
                 if (this.target != null && !this.enderman.isPassenger()) {
                     if (this.enderman.isBeingStaredBy((Player)this.target)) {
-                        if (this.target.distanceToSqr(this.enderman) < 16.0) {
+                        if (this.target.distanceToSqr(this.enderman) < 16.0 && this.enderman.tryEscape(com.destroystokyo.paper.event.entity.EndermanEscapeEvent.Reason.STARE)) { // Paper - EndermanEscapeEvent
                             this.enderman.teleport();
                         }
 
@@ -603,15 +_,18 @@
             int floor1 = Mth.floor(this.enderman.getY() + random.nextDouble() * 3.0);
             int floor2 = Mth.floor(this.enderman.getZ() - 2.0 + random.nextDouble() * 4.0);
             BlockPos blockPos = new BlockPos(floor, floor1, floor2);
-            BlockState blockState = level.getBlockState(blockPos);
+            BlockState blockState = level.getBlockStateIfLoaded(blockPos); // Paper - Prevent endermen from loading chunks
+            if (blockState == null) return; // Paper - Prevent endermen from loading chunks
             Vec3 vec3 = new Vec3(this.enderman.getBlockX() + 0.5, floor1 + 0.5, this.enderman.getBlockZ() + 0.5);
             Vec3 vec31 = new Vec3(floor + 0.5, floor1 + 0.5, floor2 + 0.5);
             BlockHitResult blockHitResult = level.clip(new ClipContext(vec3, vec31, ClipContext.Block.OUTLINE, ClipContext.Fluid.NONE, this.enderman));
             boolean flag = blockHitResult.getBlockPos().equals(blockPos);
             if (blockState.is(BlockTags.ENDERMAN_HOLDABLE) && flag) {
+                if (org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(this.enderman, blockPos, blockState.getFluidState().createLegacyBlock())) { // CraftBukkit - Place event // Paper - fix wrong block state
                 level.removeBlock(blockPos, false);
                 level.gameEvent(GameEvent.BLOCK_DESTROY, blockPos, GameEvent.Context.of(this.enderman, blockState));
                 this.enderman.setCarriedBlock(blockState.getBlock().defaultBlockState());
+                } // CraftBukkit
             }
         }
     }
