// Requirements:
        // - ✔ Try to not move a lot vertically
        // - ✔ If territorial: Stay close to the spawn point
        // - ✔ If air habitat: Don't go above maxHeight blocks above surface
        // - ✔ If land habitat: Don't walk into water, prefer surface
        // - ~~If cave habitat: Prefer caves~~
        // - ✔ If water habitat: Don't walk onto land
        // - ✔ Try not to fall from very large heights. Try not to fall from any large heights if entity has FallDamage
        // - ✔ Prefer preferredLightLevel
        // - ✔ If land habitat: Must be above a block the entity can stand on
        // - ✔ if failed searches is high, reduce wander range
        public Vec3d loadNextWanderTarget()
        {
            EnumHabitat habitat       = entity.Properties.Habitat;
            bool        canFallDamage = entity.Properties.FallDamage;
            bool        territorial   = StayCloseToSpawn;
            int         tries         = 9;
            Vec4d       bestTarget    = null;
            Vec4d       curTarget     = new Vec4d();
            BlockPos    tmpPos        = new BlockPos();

            if (FailedConsecutivePathfinds > 10)
            {
                WanderRangeMul = Math.Max(0.1f, WanderRangeMul * 0.9f);
            }
            else
            {
                WanderRangeMul = Math.Min(1, WanderRangeMul * 1.1f);
                if (rand.NextDouble() < 0.05)
                {
                    WanderRangeMul = Math.Min(1, WanderRangeMul * 1.5f);
                }
            }

            float  wRangeMul = WanderRangeMul;
            double dx, dy, dz;

            if (rand.NextDouble() < 0.05)
            {
                wRangeMul *= 3;
            }

            while (tries-- > 0)
            {
                dx = wanderRangeHorizontal.nextFloat() * (rand.Next(2) * 2 - 1) * wRangeMul;
                dy = wanderRangeVertical.nextFloat() * (rand.Next(2) * 2 - 1) * wRangeMul;
                dz = wanderRangeHorizontal.nextFloat() * (rand.Next(2) * 2 - 1) * wRangeMul;

                curTarget.X = entity.ServerPos.X + dx;
                curTarget.Y = entity.ServerPos.Y + dy;
                curTarget.Z = entity.ServerPos.Z + dz;
                curTarget.W = 1;

                if (StayCloseToSpawn)
                {
                    double distToEdge = curTarget.SquareDistanceTo(SpawnPosition) / (MaxDistanceToSpawn * MaxDistanceToSpawn);
                    // Prefer staying close to spawn
                    curTarget.W = 1 - distToEdge;
                }

                Block block;


                switch (habitat)
                {
                case EnumHabitat.Air:
                    int rainMapY = world.BlockAccessor.GetRainMapHeightAt((int)curTarget.X, (int)curTarget.Z);
                    // Don't fly above max height
                    curTarget.Y = Math.Min(curTarget.Y, rainMapY + maxHeight);

                    // Cannot be in water
                    block = entity.World.BlockAccessor.GetBlock((int)curTarget.X, (int)curTarget.Y, (int)curTarget.Z);
                    if (block.IsLiquid())
                    {
                        curTarget.W = 0;
                    }
                    break;

                case EnumHabitat.Land:
                    curTarget.Y = moveDownToFloor((int)curTarget.X, curTarget.Y, (int)curTarget.Z);
                    // No floor found
                    if (curTarget.Y < 0)
                    {
                        curTarget.W = 0;
                    }
                    else
                    {
                        // Does not like water
                        block = entity.World.BlockAccessor.GetBlock((int)curTarget.X, (int)curTarget.Y, (int)curTarget.Z);
                        if (block.IsLiquid())
                        {
                            curTarget.W /= 2;
                        }

                        // Lets make a straight line plot to see if we would fall off a cliff
                        bool stop     = false;
                        bool willFall = false;

                        float angleHor          = (float)Math.Atan2(dx, dz) + GameMath.PIHALF;
                        Vec3d target1BlockAhead = curTarget.XYZ.Ahead(1, 0, angleHor);
                        Vec3d startAhead        = entity.ServerPos.XYZ.Ahead(1, 0, angleHor); // Otherwise they are forever stuck if they stand over the edge

                        int prevY = (int)startAhead.Y;

                        GameMath.BresenHamPlotLine2d((int)startAhead.X, (int)startAhead.Z, (int)target1BlockAhead.X, (int)target1BlockAhead.Z, (x, z) =>
                        {
                            if (stop)
                            {
                                return;
                            }

                            double nowY = moveDownToFloor(x, prevY, z);

                            // Not more than 4 blocks down
                            if (nowY < 0 || prevY - nowY > 4)
                            {
                                willFall = true;
                                stop     = true;
                            }

                            // Not more than 2 blocks up
                            if (nowY - prevY > 2)
                            {
                                stop = true;
                            }

                            prevY = (int)nowY;
                        });

                        if (willFall)
                        {
                            curTarget.W = 0;
                        }
                    }
                    break;

                case EnumHabitat.Sea:
                    block = entity.World.BlockAccessor.GetBlock((int)curTarget.X, (int)curTarget.Y, (int)curTarget.Z);
                    if (!block.IsLiquid())
                    {
                        curTarget.W = 0;
                    }
                    break;
                }

                if (curTarget.W > 0)
                {
                    // Try to not hug the wall so much
                    for (int i = 0; i < BlockFacing.HORIZONTALS.Length; i++)
                    {
                        BlockFacing face = BlockFacing.HORIZONTALS[i];
                        block = entity.World.BlockAccessor.GetBlock((int)curTarget.X + face.Normali.X, (int)curTarget.Y, (int)curTarget.Z + face.Normali.Z);
                        if (block.SideSolid[face.Opposite.Index])
                        {
                            curTarget.W *= 0.5;
                        }
                    }
                }


                if (preferredLightLevel != null)
                {
                    tmpPos.Set((int)curTarget.X, (int)curTarget.Y, (int)curTarget.Z);
                    int lightdiff = Math.Abs((int)preferredLightLevel - entity.World.BlockAccessor.GetLightLevel(tmpPos, EnumLightLevelType.MaxLight));

                    curTarget.W /= Math.Max(1, lightdiff);
                }

                if (bestTarget == null || curTarget.W > bestTarget.W)
                {
                    bestTarget = new Vec4d(curTarget.X, curTarget.Y, curTarget.Z, curTarget.W);
                }
            }


            if (bestTarget.W > 0)
            {
                //double bla = bestTarget.Y;
                //bestTarget.Y += 1;
                //dx = bestTarget.X - entity.ServerPos.X;
                //dz = bestTarget.Z - entity.ServerPos.Z;
                //Vec3d sadf = bestTarget.XYZ.Ahead(1, 0, (float)Math.Atan2(dx, dz) + GameMath.PIHALF);

                /*(entity.Api as ICoreServerAPI).World.HighlightBlocks(world.AllOnlinePlayers[0], 10, new List<BlockPos>() {
                 * new BlockPos((int)bestTarget.X, (int)bestTarget.Y, (int)bestTarget.Z) }, new List<int>() { ColorUtil.ColorFromRgba(0, 255, 0, 80) }, EnumHighlightBlocksMode.Absolute, EnumHighlightShape.Arbitrary);
                 * (entity.Api as ICoreServerAPI).World.HighlightBlocks(world.AllOnlinePlayers[0], 11, new List<BlockPos>() {
                 * new BlockPos((int)sadf.X, (int)sadf.Y, (int)sadf.Z) }, new List<int>() { ColorUtil.ColorFromRgba(0, 255, 255, 180) }, EnumHighlightBlocksMode.Absolute, EnumHighlightShape.Arbitrary);*/

                //bestTarget.Y = bla;


                FailedConsecutivePathfinds = Math.Max(FailedConsecutivePathfinds - 3, 0);
                return(bestTarget.XYZ);
            }

            FailedConsecutivePathfinds++;
            return(null);
        }