示例#1
0
        static void Prefix(Pathfinder __instance, ref Vector3 startPos, ref Vector3 endPos, int teamId)
        {
            bool flagStart = false;
            bool flagEnd   = false;

            Cell start = World.inst.GetCellData(startPos);
            Cell end   = World.inst.GetCellData(endPos);

            Pathfinder.Node newStart = null;
            Pathfinder.Node newEnd   = null;

            if (start != null)
            {
                if (PathingManager.BlockedCompletely(start))
                {
                    newStart = typeof(Pathfinder).GetMethod("SearchForClosestUnblockedCell", BindingFlags.Instance | BindingFlags.NonPublic).Invoke(__instance, new object[]
                    {
                        (int)startPos.x,
                        (int)startPos.z,
                        startPos,
                        endPos,
                        teamId
                    }) as Pathfinder.Node;

                    if (newStart != null)
                    {
                        flagStart = true;
                    }
                }
            }
            if (end != null)
            {
                if (PathingManager.BlockedCompletely(end))
                {
                    newEnd = typeof(Pathfinder).GetMethod("SearchForClosestUnblockedCell", BindingFlags.Instance | BindingFlags.NonPublic).Invoke(__instance, new object[]
                    {
                        (int)endPos.x,
                        (int)endPos.z,
                        endPos,
                        startPos,
                        teamId
                    }) as Pathfinder.Node;

                    if (newEnd != null)
                    {
                        flagEnd = true;
                    }
                }
            }

            if (flagStart)
            {
                startPos = newStart.cell.Center;
            }
            if (flagEnd)
            {
                endPos = newEnd.cell.Center;
            }
        }
示例#2
0
 static bool Prefix(Pathfinder.Node node, Pathfinder.Node parent)
 {
     if (BlocksPathDirectional(parent.cell, node.cell))
     {
         return(false);
     }
     return(true);
 }
示例#3
0
        public bool ConvertDefeated(Unit unit)
        {
            // Check if this unit can convert
            if (unit.Resting || unit.Energy <= unit.Job.ActionCost)
            {
                return(false);
            }

            // Get an array of tiles containing units to convert
            HashSet <Tile> targets = this.Game.Tiles.Where(t => t.Unit != null && t.Unit.Owner == null && t.Unit.MovementTarget == null).ToHashSet();

            // If not next to a target
            if (!unit.Tile.GetNeighbors().Intersect(targets).Any())
            {
                // Find a path to the nearest target
                Queue <Pathfinder.Node <Tile> > path = new Queue <Pathfinder.Node <Tile> >(Pathfinder.FindPath(new[] { unit.Tile }, targets, this.GetNeighbors, this.GetCost));
                if (!path.Any())
                {
                    return(false);
                }

                // First node is the unit's tile
                path.Dequeue();

                // Move along the path, but don't move onto the target
                while (unit.Moves > 0 && path.Count > 1)
                {
                    Pathfinder.Node <Tile> cur = path.Dequeue();
                    unit.Move(cur.Value);
                }
            }

            // Check if next to a target
            Tile target = unit.Tile.GetNeighbors().Intersect(targets).FirstOrDefault();

            if (!unit.Acted && target != null)
            {
                // Convert
                unit.Convert(target);
                this.AI.UnitsToAct.Enqueue(target.Unit);
            }
            else if (target == null)
            {
                return(true);
            }

            return(false);
        }
示例#4
0
        public bool Harvest(Unit unit)
        {
            // Check if this unit can gather food
            if (unit.Resting || unit.Energy <= unit.Job.ActionCost || unit.CarryLeft <= 0)
            {
                return(false);
            }

            // Get an array of food tiles
            HashSet <Tile> targets = this.Game.Tiles.Where(t => (t.HarvestRate > 0 && t.TurnsToHarvest == 0) || (t.Structure?.Type == "shelter" && t.Structure?.Owner == this.Player.Opponent)).ToHashSet();

            // If not next to or on a target already
            if (!unit.Tile.GetNeighbors().Concat(new[] { unit.Tile }).Intersect(targets).Any())
            {
                // Find a path to the nearest target
                Queue <Pathfinder.Node <Tile> > path = new Queue <Pathfinder.Node <Tile> >(Pathfinder.FindPath(new[] { unit.Tile }, targets, this.GetNeighbors, this.GetCost));
                if (!path.Any())
                {
                    return(false);
                }

                // First node is the unit's tile
                path.Dequeue();

                // Move along the path, but don't move onto the target
                while (unit.Moves > 0 && path.Count > 1)
                {
                    Pathfinder.Node <Tile> cur = path.Dequeue();
                    unit.Move(cur.Value);
                }
            }

            // Check if next to or on a target
            Tile target = unit.Tile.GetNeighbors().Concat(new[] { unit.Tile }).Intersect(targets).FirstOrDefault();

            if (!unit.Acted && target != null)
            {
                // Harvest
                unit.Harvest(target);
            }
            else if (target == null)
            {
                return(true);
            }

            return(false);
        }
示例#5
0
        public bool Deconstruct(Unit unit)
        {
            // Check if this unit can gather materials
            if (unit.Resting || unit.Energy <= unit.Job.ActionCost || unit.CarryLeft <= 0)
            {
                return(false);
            }

            // Get an array of tiles containing structures to deconstruct
            HashSet <Tile> targets = this.Game.Tiles.Where(t => t.Structure != null && t.Structure.Type != "road" && t.Structure.Owner != this.Player).ToHashSet();

            // If not next to a target
            if (!unit.Tile.GetNeighbors().Intersect(targets).Any())
            {
                // Find a path to the nearest target
                Queue <Pathfinder.Node <Tile> > path = new Queue <Pathfinder.Node <Tile> >(Pathfinder.FindPath(new[] { unit.Tile }, targets, this.GetNeighbors, this.GetCost));
                if (!path.Any())
                {
                    return(false);
                }

                // First node is the unit's tile
                path.Dequeue();

                // Move along the path, but don't move onto the target
                while (unit.Moves > 0 && path.Count > 1)
                {
                    Pathfinder.Node <Tile> cur = path.Dequeue();
                    unit.Move(cur.Value);
                }
            }

            // Check if next to a target
            Tile target = unit.Tile.GetNeighbors().Intersect(targets).FirstOrDefault();

            if (!unit.Acted && target != null)
            {
                // Deconstruct
                unit.Deconstruct(target);
            }
            else if (target == null)
            {
                return(true);
            }

            return(false);
        }
示例#6
0
        public bool ConvertRoad(Unit unit)
        {
            // Check if this unit can convert
            if (unit.Resting || unit.Energy <= unit.Job.ActionCost)
            {
                return(false);
            }

            // Get an array of pathable road tiles
            HashSet <Tile> targets = this.Game.Tiles.Where(t => (t == unit.Tile || t.IsPathable()) && t.Structure?.Type == "road").ToHashSet();

            // If not on a target already
            if (!targets.Contains(unit.Tile))
            {
                // Find a path to the nearest target
                Queue <Pathfinder.Node <Tile> > path = new Queue <Pathfinder.Node <Tile> >(Pathfinder.FindPath(new[] { unit.Tile }, targets, this.GetNeighbors, this.GetCost));
                if (!path.Any())
                {
                    return(false);
                }

                // First node is the unit's tile
                path.Dequeue();

                // Move along the path
                while (unit.Moves > 0 && path.Any())
                {
                    Pathfinder.Node <Tile> cur = path.Dequeue();
                    unit.Move(cur.Value);
                }
            }

            // Try to convert a unit
            Tile target = unit.Tile.GetNeighbors().FirstOrDefault(t => t.Unit != null && t.Unit.Owner == null);

            if (!unit.Acted && target != null)
            {
                unit.Convert(target);
                this.AI.UnitsToAct.Enqueue(target.Unit);
            }
            else if (target == null)
            {
                return(true);
            }

            return(false);
        }
示例#7
0
    public static int constructor(IntPtr l)
    {
        int result;

        try
        {
            Pathfinder.Node o = new Pathfinder.Node();
            LuaObject.pushValue(l, true);
            LuaObject.pushValue(l, o);
            result = 2;
        }
        catch (Exception e)
        {
            result = LuaObject.error(l, e);
        }
        return(result);
    }
示例#8
0
    public static int Reinitialize(IntPtr l)
    {
        int result;

        try
        {
            Pathfinder.Node node = (Pathfinder.Node)LuaObject.checkSelf(l);
            node.Reinitialize();
            LuaObject.pushValue(l, true);
            result = 1;
        }
        catch (Exception e)
        {
            result = LuaObject.error(l, e);
        }
        return(result);
    }
示例#9
0
    public static int get_child(IntPtr l)
    {
        int result;

        try
        {
            Pathfinder.Node node = (Pathfinder.Node)LuaObject.checkSelf(l);
            LuaObject.pushValue(l, true);
            LuaObject.pushValue(l, node.child);
            result = 2;
        }
        catch (Exception e)
        {
            result = LuaObject.error(l, e);
        }
        return(result);
    }
示例#10
0
        public bool Rest(Unit unit)
        {
            // Check if this unit should rest
            if (Math.Abs(unit.Energy - 100) < 1)
            {
                return(false);
            }

            // If not already in range of a shelter
            if (!unit.Tile.InRange("shelter", this.Player))
            {
                // Find a path to the nearest tile in range of a shelter
                Queue <Pathfinder.Node <Tile> > path = new Queue <Pathfinder.Node <Tile> >(Pathfinder.FindPath(new[] { unit.Tile }, this.Game.Tiles.Where(t => t.InRange("shelter", this.Player) && t.IsPathable()), this.GetNeighbors, this.GetCost));
                if (!path.Any())
                {
                    return(false);
                }

                // First node is the unit's tile
                path.Dequeue();

                // Move along the path
                while (unit.Moves > 0 && path.Any())
                {
                    Pathfinder.Node <Tile> cur = path.Dequeue();
                    unit.Move(cur.Value);
                }

                // Check if near the shelter, and return if not
                if (path.Any())
                {
                    return(true);
                }
            }

            // Rest
            unit.Resting = true;
            double energyBefore = unit.Energy;

            unit.Rest();
            Logger.Log($"{unit} rested for {unit.Energy - energyBefore} energy, normal is {unit.Job.RegenRate}.");

            return(true);
        }
示例#11
0
        public bool PickupFood(Unit unit)
        {
            // Check if this unit can pickup food
            if (unit.Resting || unit.Energy <= 0 || unit.CarryLeft <= 0)
            {
                return(false);
            }

            // Get an array of tiles containing food on the ground
            HashSet <Tile> targets = this.Game.Tiles.Where(t => t.Food > 0).ToHashSet();

            // If not next to or on a target
            if (!unit.Tile.GetNeighbors().Concat(new[] { unit.Tile }).Intersect(targets).Any())
            {
                // Find a path to the nearest target
                Queue <Pathfinder.Node <Tile> > path = new Queue <Pathfinder.Node <Tile> >(Pathfinder.FindPath(new[] { unit.Tile }, targets, this.GetNeighbors, this.GetCost));
                if (!path.Any())
                {
                    return(false);
                }

                // First node is the unit's tile
                path.Dequeue();

                // Move along the path, but don't move onto the target
                while (unit.Moves > 0 && path.Count > 1)
                {
                    Pathfinder.Node <Tile> cur = path.Dequeue();
                    unit.Move(cur.Value);
                }
            }

            // Check if next to or on a target
            Tile target = unit.Tile.GetNeighbors().Concat(new[] { unit.Tile }).Intersect(targets).FirstOrDefault();

            if (target != null)
            {
                // Pickup food
                unit.Pickup(target, "food");
                return(false);
            }

            return(true);
        }
示例#12
0
        public bool StoreFood(Unit unit)
        {
            // Check if this unit can store food
            if (unit.Resting || unit.Food == 0)
            {
                return(false);
            }

            // Get an array of tiles containing friendly shelters
            HashSet <Tile> targets = this.Game.Tiles.Where(t => t.Structure?.Owner == this.Player && t.Structure?.Type == "shelter").ToHashSet();

            // If not next to a target
            if (!unit.Tile.GetNeighbors().Concat(new[] { unit.Tile }).Intersect(targets).Any())
            {
                // Find a path to the nearest target
                Queue <Pathfinder.Node <Tile> > path = new Queue <Pathfinder.Node <Tile> >(Pathfinder.FindPath(new[] { unit.Tile }, targets, this.GetNeighbors, this.GetCost));
                if (!path.Any())
                {
                    return(false);
                }

                // First node is the unit's tile
                path.Dequeue();

                // Move along the path, but don't move onto the target
                while (unit.Moves > 0 && path.Count > 1)
                {
                    Pathfinder.Node <Tile> cur = path.Dequeue();
                    unit.Move(cur.Value);
                }
            }

            // Check if next to or on a target
            Tile target = unit.Tile.GetNeighbors().Concat(new[] { unit.Tile }).Intersect(targets).FirstOrDefault();

            if (target != null)
            {
                // Drop food
                unit.Drop(target, "food");
                return(false);
            }

            return(true);
        }
示例#13
0
    public static int set_parent(IntPtr l)
    {
        int result;

        try
        {
            Pathfinder.Node node = (Pathfinder.Node)LuaObject.checkSelf(l);
            Pathfinder.Node parent;
            LuaObject.checkType <Pathfinder.Node>(l, 2, out parent);
            node.parent = parent;
            LuaObject.pushValue(l, true);
            result = 1;
        }
        catch (Exception e)
        {
            result = LuaObject.error(l, e);
        }
        return(result);
    }
示例#14
0
    public static int set_m_UserState(IntPtr l)
    {
        int result;

        try
        {
            Pathfinder.Node node = (Pathfinder.Node)LuaObject.checkSelf(l);
            PathNode        userState;
            LuaObject.checkType <PathNode>(l, 2, out userState);
            node.m_UserState = userState;
            LuaObject.pushValue(l, true);
            result = 1;
        }
        catch (Exception e)
        {
            result = LuaObject.error(l, e);
        }
        return(result);
    }
示例#15
0
        public bool AttackEnemies(Unit unit)
        {
            // Check if this unit can/should attack
            if (unit.Resting || unit.Job.Title != "soldier" || unit.Energy <= 50 || unit.Acted)
            {
                return(false);
            }

            // Get an array of food tiles
            HashSet <Tile> targets = unit.Owner.Opponent.Units.Select(u => u.Tile).ToHashSet();

            // If not next to a target already
            if (!unit.Tile.GetNeighbors().Intersect(targets).Any())
            {
                // Find a path to the nearest target
                Queue <Pathfinder.Node <Tile> > path = new Queue <Pathfinder.Node <Tile> >(Pathfinder.FindPath(new[] { unit.Tile }, targets, this.GetNeighbors, this.GetCost));
                if (!path.Any())
                {
                    return(false);
                }

                // First node is the unit's tile
                path.Dequeue();

                // Move along the path, but don't move onto the target
                while (unit.Moves > 0 && path.Count > 1)
                {
                    Pathfinder.Node <Tile> cur = path.Dequeue();
                    unit.Move(cur.Value);
                }
            }

            // Check if next to or on a target
            Tile target = unit.Tile.GetNeighbors().Concat(new[] { unit.Tile }).Intersect(targets).FirstOrDefault();

            if (!unit.Acted && target != null)
            {
                // Attack
                return(unit.Attack(target));
            }

            return(false);
        }
示例#16
0
    public static int set_f(IntPtr l)
    {
        int result;

        try
        {
            Pathfinder.Node node = (Pathfinder.Node)LuaObject.checkSelf(l);
            int             f;
            LuaObject.checkType(l, 2, out f);
            node.f = f;
            LuaObject.pushValue(l, true);
            result = 1;
        }
        catch (Exception e)
        {
            result = LuaObject.error(l, e);
        }
        return(result);
    }
示例#17
0
 static void Postfix(Pathfinder __instance, ref Pathfinder.Node __result, int sx, int sz, Vector3 start, Vector3 end, int teamId)
 {
     try
     {
         Cell  cell = null;
         float num  = float.MaxValue;
         int   num2 = 1;
         int   num3 = Mathf.Clamp(sx - num2, 0, World.inst.GridWidth - 1);
         int   num4 = Mathf.Clamp(sx + num2, 0, World.inst.GridWidth - 1);
         int   num5 = Mathf.Clamp(sz - num2, 0, World.inst.GridHeight - 1);
         int   num6 = Mathf.Clamp(sz + num2, 0, World.inst.GridHeight - 1);
         for (int i = num3; i <= num4; i++)
         {
             for (int j = num5; j <= num6; j++)
             {
                 Cell cellDataUnsafe = World.inst.GetCellDataUnsafe(i, j);
                 if (cellDataUnsafe != null)
                 {
                     if (!__instance.blocksPath(cellDataUnsafe, teamId) && !PathingManager.BlockedCompletely(cellDataUnsafe))
                     {
                         float num7 = Mathff.DistSqrdXZ(cellDataUnsafe.Center, start);
                         if (num7 < num)
                         {
                             num  = num7;
                             cell = cellDataUnsafe;
                         }
                     }
                 }
             }
         }
         if (cell != null)
         {
             __result = __instance.GetFieldValue <Pathfinder.Node[, ]>("pathGrid")[cell.x, cell.z];
             return;
         }
         __result = null;
     }
     catch (Exception ex)
     {
         DebugExt.HandleException(ex);
     }
 }
示例#18
0
        public bool MoveToRoadShelter(Unit unit)
        {
            // Check if this unit can move
            if (unit.Resting || unit.Moves == 0)
            {
                return(false);
            }

            // Get the shelter closest to the road
            HashSet <Tile> targets = (from s in this.Player.Structures
                                      where s.Type == "shelter" && s.Tile != null
                                      orderby Math.Abs(this.Game.MapHeight / 2F - s.Tile.Y)
                                      select s.Tile)
                                     .SelectMany(t => t.GetNeighbors())
                                     .Where(t => (t.Unit == unit || t.IsPathable()) && t.Structure == null)
                                     .Take(1)
                                     .ToHashSet();

            // If not on a target already
            if (!targets.Contains(unit.Tile))
            {
                // Find a path to the nearest target
                Queue <Pathfinder.Node <Tile> > path = new Queue <Pathfinder.Node <Tile> >(Pathfinder.FindPath(new[] { unit.Tile }, targets, this.GetNeighbors, this.GetCost));
                if (!path.Any())
                {
                    return(false);
                }

                // First node is the unit's tile
                path.Dequeue();

                // Move along the path
                while (unit.Moves > 0 && path.Any())
                {
                    Pathfinder.Node <Tile> cur = path.Dequeue();
                    unit.Move(cur.Value);
                }
            }

            return(false);
        }
示例#19
0
        public bool DefendCat(Unit unit)
        {
            // Check if this unit should defend
            if (unit.Resting || unit.Energy <= 25)
            {
                return(false);
            }

            // Ignore these checks if opponent is rushing
            if (!this.OpponentRushing)
            {
                const int defenders = 1;

                // Make sure the right number of units defend the cat
                if (this.Player.Cat.Squad.Count > defenders + 1)
                {
                    return(false);
                }

                // Make sure if the cat is defended and this unit isn't defending it, don't defend it
                if (this.Player.Cat.Squad.Count == defenders + 1 && !this.Player.Cat.Squad.Contains(unit))
                {
                    return(false);
                }
            }

            // Get an array with the cat's tile (needs to be an IEnumerable<Tile>)
            HashSet <Tile> targets = this.Player.Cat.Tile.GetNeighbors().Where(t => t.Structure?.Owner != this.Player && (t.Unit == unit || t.IsPathable()) && t.InRange("shelter", this.Player)).ToHashSet();

            if (!targets.Any())
            {
                targets = this.Player.Cat.Tile.GetNeighbors().Where(t => t.Structure?.Owner != this.Player && (t.Unit == unit || t.IsPathable())).ToHashSet();
            }
            if (!targets.Any())
            {
                targets = this.Player.Units.Where(u => this.Player.Cat.Squad.Contains(u)).SelectMany(u => u.Tile.GetNeighbors().Where(t => t.Unit == unit || t.IsPathable())).ToHashSet();
            }

            // If not on a target already
            if (!targets.Contains(unit.Tile))
            {
                // Find a path to the nearest target
                Queue <Pathfinder.Node <Tile> > path = new Queue <Pathfinder.Node <Tile> >(Pathfinder.FindPath(new[] { unit.Tile }, targets, this.GetNeighbors, this.GetCost));
                if (!path.Any())
                {
                    return(false);
                }

                // First node is the unit's tile
                path.Dequeue();

                // Move along the path
                while (unit.Moves > 0 && path.Any())
                {
                    Pathfinder.Node <Tile> cur = path.Dequeue();
                    unit.Move(cur.Value);
                }
            }

            return(true);
        }
示例#20
0
        public bool ChangeJobs(Unit unit)
        {
            // Check if this unit can change jobs
            if (unit.Energy < 100)
            {
                return(false);
            }

            // Get an array of tiles in range of the cat
            HashSet <Tile> targets = this.Game.Tiles.Where(t => t.InRange(this.Player.Cat, 1) && t.IsPathable()).ToHashSet();

            // If not on a target already
            if (!targets.Contains(unit.Tile))
            {
                // Find a path to the nearest target
                Queue <Pathfinder.Node <Tile> > path = new Queue <Pathfinder.Node <Tile> >(Pathfinder.FindPath(new[] { unit.Tile }, targets, this.GetNeighbors, this.GetCost));
                if (!path.Any())
                {
                    return(false);
                }

                // First node is the unit's tile
                path.Dequeue();

                // Move along the path
                while (unit.Moves > 0 && path.Any())
                {
                    Pathfinder.Node <Tile> cur = path.Dequeue();
                    unit.Move(cur.Value);
                }
            }

            // Check if on a target
            if (!unit.Acted && targets.Contains(unit.Tile))
            {
                if (this.AI.CountUnits("gatherer") < this.Player.Units.Count / this.AI.UnitsPerGatherer)
                {
                    unit.ChangeJob("gatherer"); // Need gatherers
                }
                else if (this.AI.CountUnits("soldier") < 1)
                {
                    unit.ChangeJob("soldier"); // Need at least one soldier
                }
                else if (this.AI.CountUnits("missionary") < 1)
                {
                    unit.ChangeJob("missionary"); // Need at least one missionary
                }
                else if (this.AI.CountUnits("builder") == 0)
                {
                    unit.ChangeJob("builder"); // Need at least one builder, but not as important
                }
                else if (this.AI.CountUnits("missionary") < 2)
                {
                    unit.ChangeJob("missionary"); // Need at least one missionary
                }
                else
                {
                    unit.ChangeJob("soldier"); // Rest are soldiers
                }
            }

            return(true);
        }
示例#21
0
        public bool ForceChangeJobs(Unit unit)
        {
            // Figure out which job to force change to
            string job = null;

            if (this.Player.Units.Count >= this.Player.Opponent.Units.Count * 2)
            {
                job = "soldier"; // Full out assault
            }
            else if (this.AI.CountUnits("missionary") == 0)
            {
                job = "missionary"; // Make sure there's a missionary
            }
            else if (this.OpponentRushing)
            {
                job = "soldier";
            }

            // Make sure a force change is necessary
            if (job == null || unit.Job.Title == job)
            {
                return(false);
            }

            // Check if this unit can change jobs, and make sure it can eventually
            if (unit.Energy < 100)
            {
                return(this.Rest(unit));
            }

            // Get an array of tiles in range of the cat
            HashSet <Tile> targets = this.Game.Tiles.Where(t => t.InRange(this.Player.Cat, 1) && t.IsPathable()).ToHashSet();

            // If not on a target already
            if (!targets.Contains(unit.Tile))
            {
                // Find a path to the nearest target
                Queue <Pathfinder.Node <Tile> > path = new Queue <Pathfinder.Node <Tile> >(Pathfinder.FindPath(new[] { unit.Tile }, targets, this.GetNeighbors, this.GetCost));
                if (!path.Any())
                {
                    return(false);
                }

                // First node is the unit's tile
                path.Dequeue();

                // Move along the path
                while (unit.Moves > 0 && path.Any())
                {
                    Pathfinder.Node <Tile> cur = path.Dequeue();
                    unit.Move(cur.Value);
                }
            }

            // Check if on a target
            if (!unit.Acted && targets.Contains(unit.Tile))
            {
                unit.ChangeJob(job);
            }

            return(true);
        }
示例#22
0
        public bool ConstructMonument(Unit unit)
        {
            // Check if this unit can construct a monument
            if (unit.Resting || unit.Energy <= unit.Job.ActionCost || unit.Materials < unit.Job.CarryLimit)
            {
                return(false);
            }

            // Sort all the tiles a monument can be constructed on by how good of a spot it is. Lower score is better
            IEnumerable <Tile> monumentSpots = this.Game.Tiles.Where(t => t.Materials > 0).ToHashSet();

            if (!monumentSpots.Any())
            {
                monumentSpots = (from t in this.Game.Tiles
                                 where t.Structure == null && t.Unit == null && t.HarvestRate == 0
                                 orderby this.GetMonumentScore(t)
                                 select t).Take(3);
            }

            HashSet <Tile> targets = monumentSpots.ToHashSet();

            foreach (Tile t in targets)
            {
                t.Log("(monument)");
            }

            // If not next to a target
            if (!unit.Tile.GetNeighbors().Intersect(targets).Any())
            {
                // Find a path to the nearest target
                Queue <Pathfinder.Node <Tile> > path = new Queue <Pathfinder.Node <Tile> >(Pathfinder.FindPath(new[] { unit.Tile }, targets, this.GetNeighbors, this.GetCost));
                if (!path.Any())
                {
                    return(false);
                }

                // First node is the unit's tile
                path.Dequeue();

                // Move along the path, but don't move onto the target
                while (unit.Moves > 0 && path.Count > 1)
                {
                    Pathfinder.Node <Tile> cur = path.Dequeue();
                    unit.Move(cur.Value);
                }
            }

            // Check if next to a target
            Tile target = unit.Tile.GetNeighbors().Intersect(targets).FirstOrDefault();

            if (!unit.Acted && target != null)
            {
                // Construct a shelter
                unit.Drop(target, "materials", this.Game.MonumentMaterials - target.Materials);
                if (target.Materials >= this.Game.MonumentMaterials)
                {
                    unit.Construct(target, "monument");
                }
            }
            else if (target == null)
            {
                return(true);
            }

            return(false);
        }