I use this code to create a entity. I spawn it but it's invisible and the AI is working correctly. I'm sure that I didn't give it invisible potion. In spigot 1.13.2,it's visible. In spigot 1.12.1,it's invisible I use EntitySpawnEvent to replace old zombie. I know it's because I forget to register it.......................... spawn code: Code (Text): public DoomsdayZombie spawnDoomsdayZombie(Location loc) { DoomsdayZombie dz = new DoomsdayZombie(loc); CraftWorld craftWorld = (CraftWorld) loc.getWorld(); craftWorld.addEntity(dz, CreatureSpawnEvent.SpawnReason.CUSTOM); return dz; } entity code: Code (Text): public class DoomsdayZombie extends EntityZombie { public ZombieTaskHandleStack taskHandle; public static Double movementSpeed; public ZombieAction action = ZombieAction.Avaiable; public DoomsdaySurvival plugin; public int stopCount = 0; public double last_X, last_Z; static { movementSpeed = Config.DOOMSDAY_ZOMBIE_SPEED; } public DoomsdayZombie(Location loc) { super(((CraftWorld) loc.getWorld()).getHandle()); this.plugin = DoomsdaySurvival.plugin; setPosition(loc.getX(), loc.getY(), loc.getZ()); taskHandle = new ZombieTaskHandleStack(plugin, this); } @Override protected void initAttributes() { super.initAttributes(); getAttributeInstance(GenericAttributes.maxHealth).setValue(Config.DOOMSDAY_ZOMBIE_HEALTH); getAttributeInstance(GenericAttributes.MOVEMENT_SPEED).setValue(movementSpeed); getAttributeInstance(GenericAttributes.FOLLOW_RANGE).setValue(Config.DOOMSDAY_ZOMBIE_FOLLOWRANGE); getAttributeInstance(GenericAttributes.ATTACK_DAMAGE).setValue(Config.DOOMSDAY_ZOMBIE_DAMAGE); } public boolean isSameWorldWithTarget() { return getBukkitEntity().getWorld().getName().equals(getTarget().getWorld().getName()); } public Location getLocation() { return ((org.bukkit.entity.Zombie) getBukkitEntity()).getLocation().clone(); } public ZombieAction getAction() { return action; } public boolean isSameHeightWithTarget() { return hasTarget() && Math.abs(getTarget().getLocation().getY() - locY) <= 0.5; } public void ifStop() { if (stopCount == 0) { last_X = locX; last_Z = locZ; } stopCount++; if (stopCount == 60) { stopCount = 0; if (getXZDistance(last_X, last_Z, locX, locZ) < 0.5 && action == ZombieAction.Avaiable) { stop(); } else { avaiable(); } } } public void stop() { action = ZombieAction.Stop; goalSelector = new PathfinderGoalSelector((world != null) && (world.methodProfiler != null) ? world.methodProfiler : null); goalSelector.a(0, new PathfinderGoalFloat(this)); targetSelector = new PathfinderGoalSelector((world != null) && (world.methodProfiler != null) ? world.methodProfiler : null); } public void avaiable() { action = ZombieAction.Avaiable; goalSelector = new PathfinderGoalSelector((world != null) && (world.methodProfiler != null) ? world.methodProfiler : null); goalSelector.a(0, new PathfinderGoalFloat(this)); goalSelector.a(1, new PathfinderGoalZombieAttack(this, 1.0D, false)); goalSelector.a(1, new DoomsdayZombieBreakPathfinderGoal(this)); goalSelector.a(1, new DoomsdayZombiePlacePathfinderGoal(this)); targetSelector = new PathfinderGoalSelector((world != null) && (world.methodProfiler != null) ? world.methodProfiler : null); targetSelector.a(2, new PathfinderGoalNearestAttackableTarget(this, EntityHuman.class, true)); if (world.spigotConfig.zombieAggressiveTowardsVillager) { targetSelector.a(3, new PathfinderGoalNearestAttackableTarget(this, EntityVillager.class, false)); } } @Override public void B_() { if (hasTarget()) { ifStop(); if (!isBlockPassable(taskHandle.getHeadBlock().getRelative(BlockFace.DOWN))) { taskHandle.push(new BlockTask(taskHandle.getHeadBlock().getRelative(BlockFace.DOWN))); } if (!isBlockPassable(taskHandle.getHeadBlock())) { taskHandle.push(new BlockTask(taskHandle.getHeadBlock())); } if (getTarget().getLocation().getY() - locY > 0.5 && (action == ZombieAction.Stop || getXZDistance(getTarget().getLocation(), this.getBukkitEntity().getLocation()) < 7)) { stop(); Location loc = getLocation(); setPosition(loc.getBlockX() + 0.5, locY, loc.getBlockZ() + 0.5); if (!isBlockPassable(taskHandle.getBlockOnHead())) { taskHandle.push(new BlockTask(taskHandle.getBlockOnHead())); } else { BlockTask task = new BlockTask(loc, 1); task.isUp = true; taskHandle.push(task); } } else { if (locY - getTarget().getLocation().getY() > 1 && getXZDistance(bukkitEntity.getLocation(), getTarget().getLocation()) < 5 && isInWater()) { Location loc = getLocation().subtract(0, 1, 0); taskHandle.push(new BlockTask(loc.getBlock())); motY = -movementSpeed; avaiable(); } else if (locY - getTarget().getLocation().getY() > 1 && getXZDistance(bukkitEntity.getLocation(), getTarget().getLocation()) < 0.3) { Location loc = getLocation().subtract(0, 1, 0); taskHandle.push(new BlockTask(loc.getBlock())); avaiable(); } else if (isSameHeightWithTarget() || getTarget().getLocation().getY() - locY > 1 && action == ZombieAction.Stop) { avaiable(); } } } else { avaiable(); foundTarget(); } taskHandle.doTick(); super.B_(); } public static Block getNMSBlock(CraftBlock b) { return (Block) NBT.doMethod(b, "getNMSBlock"); } public static IBlockData getBlockNMS(CraftBlock b) { return (IBlockData) NBT.doMethod(b, "getData0"); } public static boolean isBlockPassable(org.bukkit.block.Block b) { return b.isEmpty(); } public void foundTarget() { if (!hasTarget()) { for (org.bukkit.entity.Entity en : bukkitEntity.getNearbyEntities(Config.DOOMSDAY_ZOMBIE_FOLLOWRANGE, Config.DOOMSDAY_ZOMBIE_FOLLOWRANGE, Config.DOOMSDAY_ZOMBIE_FOLLOWRANGE)) { if (en instanceof org.bukkit.entity.LivingEntity) { if (en instanceof org.bukkit.entity.Player) { if (((org.bukkit.entity.Player) en).getGameMode() != GameMode.CREATIVE) { setTarget((LivingEntity) en); return; } } if (en instanceof org.bukkit.entity.Villager || en instanceof org.bukkit.entity.IronGolem) { setTarget((LivingEntity) en); return; } } } } } public static double getXZDistance(double x1, double z1, double x2, double z2) { return Math.sqrt((x1 - x2) * (x1 - x2) + (z1 - z2) * (z1 - z2)); } public static double getXZDistance(Location loc1, Location loc2) { return Math.sqrt((loc1.getX() - loc2.getX()) * (loc1.getX() - loc2.getX()) + (loc1.getZ() - loc2.getZ()) * (loc1.getZ() - loc2.getZ())); } public org.bukkit.entity.LivingEntity getTarget() { return ((org.bukkit.entity.Zombie) getBukkitEntity()).getTarget(); } public boolean hasTarget() { if (((org.bukkit.entity.Zombie) getBukkitEntity()).getTarget() != null) { if (!((org.bukkit.entity.Zombie) getBukkitEntity()).getTarget().isDead() && isSameWorldWithTarget()) { return true; } } return false; } public void setTarget(org.bukkit.entity.LivingEntity le) { ((org.bukkit.entity.Zombie) getBukkitEntity()).setTarget(le); } @Override public void n() { goalSelector.a(0, new PathfinderGoalFloat(this)); goalSelector.a(1, new PathfinderGoalZombieAttack(this, 1.0D, false)); targetSelector.a(2, new PathfinderGoalNearestAttackableTarget(this, EntityHuman.class, true)); if (world.spigotConfig.zombieAggressiveTowardsVillager) { targetSelector.a(3, new PathfinderGoalNearestAttackableTarget(this, EntityVillager.class, false)); } goalSelector.a(1, new DoomsdayZombieBreakPathfinderGoal(this)); goalSelector.a(1, new DoomsdayZombiePlacePathfinderGoal(this)); } public enum ZombieAction { Avaiable, Stop; } }