Example #1
0
        // Inverts GetMoveActions. So, returns the list of move actions whose target is the current voxel.
        // Very, very slow.
        public IEnumerable <MoveAction> GetInverseMoveActions(MoveState currentstate, List <GameComponent> teleportObjects)
        {
            if (Parent == null)
            {
                yield break;
            }

            if (Creature == null)
            {
                yield break;
            }

            var current = currentstate.Voxel;

            if (Can(MoveType.Teleport))
            {
                foreach (var obj in teleportObjects)
                {
                    if ((obj.Position - current.WorldPosition).LengthSquared() > 2)
                    {
                        continue;
                    }

                    for (int dx = -TeleportDistance; dx <= TeleportDistance; dx++)
                    {
                        for (int dz = -TeleportDistance; dz <= TeleportDistance; dz++)
                        {
                            for (int dy = -TeleportDistance; dy <= TeleportDistance; dy++)
                            {
                                if (dx * dx + dy * dy + dz * dz > TeleportDistanceSquared)
                                {
                                    continue;
                                }
                                VoxelHandle teleportNeighbor = new VoxelHandle(Parent.World.ChunkManager, current.Coordinate + new GlobalVoxelOffset(dx, dy, dz));
                                var         adjacent         = VoxelHelpers.GetNeighbor(teleportNeighbor, new GlobalVoxelOffset(0, -1, 0));
                                if (teleportNeighbor.IsValid && teleportNeighbor.IsEmpty && adjacent.IsValid && adjacent.IsEmpty)
                                {
                                    yield return(new MoveAction()
                                    {
                                        InteractObject = obj,
                                        Diff = new Vector3(dx, dx, dz),
                                        SourceVoxel = teleportNeighbor,
                                        DestinationState = currentstate,
                                        MoveType = MoveType.Teleport,
                                        CostMultiplier = 1.0f
                                    });
                                }
                            }
                        }
                    }
                }
            }

            var storage = new MoveActionTempStorage();

            foreach (var v in VoxelHelpers.EnumerateCube(current.Coordinate)
                     .Select(n => new VoxelHandle(current.Chunk.Manager, n))
                     .Where(h => h.IsValid))
            {
                foreach (var a in GetMoveActions(new MoveState()
                {
                    Voxel = v
                }, teleportObjects, storage).Where(a => a.DestinationState == currentstate))
                {
                    yield return(a);
                }

                if (!Can(MoveType.RideVehicle))
                {
                    continue;
                }

                // Now that dwarfs can ride vehicles, the inverse of the move actions becomes extremely complicated. We must now
                // iterate through all rails intersecting every neighbor and see if we can find a connection from that rail to this one.
                // Further, we must iterate through the entire rail network and enumerate all possible directions in and out of that rail.
                // Yay!

                // Actually - why not just not bother with rails when inverse pathing, since it should only be invoked when forward pathing fails anyway?
                // Also NOT hacking in inverse elevators!

                /*
                 * var bodies = new HashSet<GameComponent>();
                 * OctTree.EnumerateItems(v.GetBoundingBox(), bodies);
                 *
                 * var rails = bodies.OfType<Rail.RailEntity>().Where(r => r.Active);
                 * foreach (var rail in rails)
                 * {
                 *  if (rail.GetContainingVoxel() != v)
                 *      continue;
                 *
                 *  foreach (var neighborRail in rail.NeighborRails.Select(neighbor => Creature.Manager.FindComponent(neighbor.NeighborID) as Rail.RailEntity))
                 *  {
                 *      var actions = GetMoveActions(new MoveState()
                 *      {
                 *          Voxel = v,
                 *          VehicleType = VehicleTypes.Rail,
                 *          Rail = rail,
                 *          PrevRail = neighborRail
                 *      }, OctTree, teleportObjects, storage);
                 *      foreach (var a in actions.Where(a => a.DestinationState == currentstate))
                 *      {
                 *          yield return a;
                 *      }
                 *  }
                 *
                 *  foreach (var a in GetMoveActions(new MoveState() { Voxel = v, VehicleType = VehicleTypes.Rail, Rail = rail, PrevRail = null }, OctTree, teleportObjects, storage).Where(a => a.DestinationState == currentstate))
                 *      yield return a;
                 * }
                 */
            }
        }
Example #2
0
        /// <summary> gets the list of actions that the creature can take from a given voxel. </summary>
        public IEnumerable <MoveAction> GetMoveActions(MoveState state, List <GameComponent> teleportObjects, MoveActionTempStorage Storage)
        {
            if (Parent == null)
            {
                yield break;
            }

            if (!state.Voxel.IsValid)
            {
                yield break;
            }

            if (Creature == null)
            {
                yield break;
            }

            if (Storage == null)
            {
                Storage = new MoveActionTempStorage();
            }

            GetNeighborhood(state.Voxel.Chunk.Manager, state.Voxel, Storage.Neighborhood);

            bool inWater          = (Storage.Neighborhood[1, 1, 1].IsValid && Storage.Neighborhood[1, 1, 1].LiquidLevel > WaterManager.inWaterThreshold);
            bool standingOnGround = (Storage.Neighborhood[1, 0, 1].IsValid && !Storage.Neighborhood[1, 0, 1].IsEmpty);
            bool topCovered       = (Storage.Neighborhood[1, 2, 1].IsValid && !Storage.Neighborhood[1, 2, 1].IsEmpty);
            bool hasNeighbors     = HasNeighbors(Storage.Neighborhood);
            bool isRiding         = state.VehicleType != VehicleTypes.None;

            var neighborHoodBounds = new BoundingBox(Storage.Neighborhood[0, 0, 0].GetBoundingBox().Min, Storage.Neighborhood[2, 2, 2].GetBoundingBox().Max);

            Storage.NeighborObjects.Clear();
            Parent.World.EnumerateIntersectingObjects(neighborHoodBounds, Storage.NeighborObjects);

            if (Can(MoveType.Teleport))
            {
                foreach (var obj in teleportObjects)
                {
                    if ((obj.Position - state.Voxel.WorldPosition).LengthSquared() < TeleportDistanceSquared)
                    {
                        yield return new MoveAction()
                               {
                                   InteractObject   = obj,
                                   MoveType         = MoveType.Teleport,
                                   SourceVoxel      = state.Voxel,
                                   DestinationState = new MoveState()
                                   {
                                       Voxel = new VoxelHandle(state.Voxel.Chunk.Manager, GlobalVoxelCoordinate.FromVector3(obj.Position))
                                   },
                                   CostMultiplier = 1.0f
                               }
                    }
                }
            }
            ;

            var successors = EnumerateSuccessors(state, state.Voxel, Storage, inWater, standingOnGround, topCovered, hasNeighbors);

            // Now, validate each move action that the creature might take.
            foreach (MoveAction v in successors)
            {
#if DEBUG
                if (!v.DestinationVoxel.IsValid)
                {
                    throw new InvalidOperationException();
                }
#endif

                var n = v.DestinationVoxel.IsValid ? v.DestinationVoxel : Storage.Neighborhood[(int)v.Diff.X, (int)v.Diff.Y, (int)v.Diff.Z];
                if (n.IsValid && (v.MoveType == MoveType.Dig || isRiding || n.IsEmpty || n.LiquidLevel > 0))
                {
                    // Do one final check to see if there is an object blocking the motion.
                    bool blockedByObject = false;
                    if (state.VehicleType == VehicleTypes.None)
                    {
                        var objectsAtNeighbor = Storage.NeighborObjects.Where(o => o.GetBoundingBox().Intersects(n.GetBoundingBox()));

                        // If there is an object blocking the motion, determine if it can be passed through.

                        foreach (var body in objectsAtNeighbor)
                        {
                            var door = body as Door;
                            // ** Doors are in the octtree, pretty sure this was always pointless -- var door = body.GetRoot().EnumerateAll().OfType<Door>().FirstOrDefault();
                            // If there is an enemy door blocking movement, we can destroy it to get through.
                            if (door != null)
                            {
                                if (
                                    Creature.World.Overworld.GetPolitics(door.TeamFaction.ParentFaction, Creature.Faction.ParentFaction)
                                    .GetCurrentRelationship() ==
                                    Relationship.Hateful)
                                {
                                    if (Can(MoveType.DestroyObject))
                                    {
                                        yield return(new MoveAction
                                        {
                                            Diff = v.Diff,
                                            MoveType = MoveType.DestroyObject,
                                            InteractObject = door,
                                            DestinationVoxel = n,
                                            SourceState = state,
                                            CostMultiplier = 1.0f // Todo: Multiply by toughness of object?
                                        });
                                    }
                                    blockedByObject = true;
                                }
                            }
                        }
                    }

                    // If no object blocked us, we can move freely as normal.
                    if (!blockedByObject && n.LiquidType != LiquidType.Lava)
                    {
                        var newAction = v;
                        newAction.SourceState      = state;
                        newAction.DestinationVoxel = n;
                        yield return(newAction);
                    }
                }
            }
        }
Example #3
0
        private IEnumerable <MoveAction> EnumerateSuccessors(
            MoveState state,
            VoxelHandle voxel,
            MoveActionTempStorage Storage,
            bool inWater,
            bool standingOnGround,
            bool topCovered,
            bool hasNeighbors)
        {
            bool isClimbing = false;

            if (state.VehicleType == VehicleTypes.Rail)
            {
                if (Can(MoveType.ExitVehicle)) // Possibly redundant... If they can ride they should be able to exit right?
                {
                    yield return(new MoveAction()
                    {
                        SourceState = state,
                        DestinationState = new MoveState()
                        {
                            VehicleType = VehicleTypes.None,
                            Voxel = state.Voxel
                        },
                        MoveType = MoveType.ExitVehicle,
                        Diff = new Vector3(1, 1, 1),
                        CostMultiplier = 1.0f
                    });
                }

                if (Can(MoveType.RideVehicle))
                {
                    foreach (var neighbor in Rail.RailHelper.EnumerateForwardNetworkConnections(state.PrevRail, state.Rail))
                    {
                        var neighborRail = Creature.Manager.FindComponent(neighbor) as Rail.RailEntity;
                        if (neighborRail == null || !neighborRail.Active)
                        {
                            continue;
                        }

                        yield return(new MoveAction()
                        {
                            SourceState = state,
                            DestinationState = new MoveState()
                            {
                                Voxel = neighborRail.GetContainingVoxel(),
                                Rail = neighborRail,
                                PrevRail = state.Rail,
                                VehicleType = VehicleTypes.Rail
                            },
                            MoveType = MoveType.RideVehicle,
                            CostMultiplier = 1.0f
                        });
                    }
                }

                yield break; // Nothing can be done without exiting the rails first.
            }

            if (CanClimb || Can(MoveType.RideVehicle))
            {
                //Climbing ladders and riding rails.

                var bodies = Storage.NeighborObjects.Where(o => o.GetBoundingBox().Intersects(voxel.GetBoundingBox()));

                // if the creature can climb objects and a ladder is in this voxel,
                // then add a climb action.
                if (CanClimb)
                {
                    var ladder = bodies.FirstOrDefault(component => component.Tags.Contains("Climbable"));

                    if (ladder != null)
                    {
                        yield return(new MoveAction
                        {
                            SourceState = state,
                            Diff = new Vector3(1, 2, 1),
                            MoveType = MoveType.Climb,
                            InteractObject = ladder,
                            CostMultiplier = 1.0f,
                            DestinationVoxel = Storage.Neighborhood[1, 2, 1]
                        });

                        if (!standingOnGround)
                        {
                            yield return(new MoveAction
                            {
                                SourceState = state,
                                Diff = new Vector3(1, 0, 1),
                                MoveType = MoveType.Climb,
                                InteractObject = ladder,
                                CostMultiplier = 1.0f,
                                DestinationVoxel = Storage.Neighborhood[1, 2, 1]
                            });
                        }
                        standingOnGround = true;
                    }
                }

                if (Can(MoveType.RideVehicle))
                {
                    var rails = bodies.OfType <Rail.RailEntity>().Where(r => r.Active);

                    if (rails.Count() > 0 && Can(MoveType.RideVehicle))
                    {
                        {
                            foreach (var rail in rails)
                            {
                                if (rail.GetContainingVoxel() != state.Voxel)
                                {
                                    continue;
                                }


                                yield return(new MoveAction()
                                {
                                    SourceState = state,
                                    DestinationState = new MoveState()
                                    {
                                        VehicleType = VehicleTypes.Rail,
                                        Rail = rail,
                                        Voxel = rail.GetContainingVoxel()
                                    },
                                    MoveType = MoveType.EnterVehicle,
                                    Diff = new Vector3(1, 1, 1),
                                    CostMultiplier = 1.0f
                                });
                            }
                        }
                    }

                    var elevators = bodies.OfType <Elevators.ElevatorShaft>().Where(r => r.Active && System.Math.Abs(r.Position.Y - voxel.Center.Y) < 0.5f);

                    foreach (var elevator in elevators)
                    {
                        foreach (var elevatorExit in Elevators.Helper.EnumerateExits(elevator.Shaft))
                        {
                            if (object.ReferenceEquals(elevator, elevatorExit.ShaftSegment))
                            {
                                continue;                                                              // Ignore exits from the segment we are entering at.
                            }
                            yield return(new MoveAction()
                            {
                                SourceState = state,
                                DestinationState = new MoveState()
                                {
                                    Voxel = elevatorExit.OntoVoxel,
                                    VehicleType = VehicleTypes.None,
                                    Tag = new Elevators.ElevatorMoveState
                                    {
                                        Entrance = elevator,
                                        Exit = elevatorExit.ShaftSegment
                                    }
                                },
                                MoveType = MoveType.RideElevator,
                                CostMultiplier = elevator.GetQueueSize() + 1.0f
                            });
                        }
                    }
                }
            }

            // If the creature can fly and is not underwater, it can fly
            // to any adjacent empty cell.
            if (CanFly && !inWater)
            {
                for (int dx = 0; dx <= 2; dx++)
                {
                    for (int dz = 0; dz <= 2; dz++)
                    {
                        for (int dy = 0; dy <= 2; dy++)
                        {
                            if (dx == 1 && dz == 1 && dy == 1)
                            {
                                continue;
                            }

                            if (Storage.Neighborhood[dx, dy, dz].IsValid && Storage.Neighborhood[dx, dy, dz].IsEmpty)
                            {
                                yield return(new MoveAction
                                {
                                    SourceState = state,
                                    Diff = new Vector3(dx, dy, dz),
                                    MoveType = MoveType.Fly,
                                    CostMultiplier = 1.0f,
                                    DestinationVoxel = Storage.Neighborhood[dx, dy, dz]
                                });
                            }
                        }
                    }
                }
            }

            // If the creature is not in water and is not standing on ground,
            // it can fall one voxel downward in free space.
            if (!inWater && !standingOnGround)
            {
                yield return(new MoveAction
                {
                    SourceState = state,
                    Diff = new Vector3(1, 0, 1),
                    MoveType = MoveType.Fall,
                    CostMultiplier = 1.0f,
                    DestinationVoxel = Storage.Neighborhood[1, 0, 1]
                });
            }

            // If the creature can climb walls and is not blocked by a voxl above.
            if (CanClimbWalls && !topCovered)
            {
                // This monstrosity is unrolling an inner loop so that we don't have to allocate an array or
                // enumerators.
                var wall = VoxelHandle.InvalidHandle;
                var n211 = Storage.Neighborhood[2, 1, 1];
                if (n211.IsValid && !n211.IsEmpty)
                {
                    wall = n211;
                }
                else
                {
                    var n011 = Storage.Neighborhood[0, 1, 1];
                    if (n011.IsValid && !n011.IsEmpty)
                    {
                        wall = n011;
                    }
                    else
                    {
                        var n112 = Storage.Neighborhood[1, 1, 2];
                        if (n112.IsValid && !n112.IsEmpty)
                        {
                            wall = n112;
                        }
                        else
                        {
                            var n110 = Storage.Neighborhood[1, 1, 0];
                            if (n110.IsValid && !n110.IsEmpty)
                            {
                                wall = n110;
                            }
                        }
                    }
                }

                if (wall.IsValid)
                {
                    isClimbing = true;
                    yield return(new MoveAction
                    {
                        SourceState = state,
                        Diff = new Vector3(1, 2, 1),
                        MoveType = MoveType.ClimbWalls,
                        ActionVoxel = wall,
                        CostMultiplier = 1.0f,
                        DestinationVoxel = Storage.Neighborhood[1, 2, 1]
                    });

                    if (!standingOnGround)
                    {
                        yield return(new MoveAction
                        {
                            SourceState = state,
                            Diff = new Vector3(1, 0, 1),
                            MoveType = MoveType.ClimbWalls,
                            ActionVoxel = wall,
                            CostMultiplier = 1.0f,
                            DestinationVoxel = Storage.Neighborhood[1, 0, 1]
                        });
                    }
                }
            }

            // If the creature either can walk or is in water, add the
            // eight-connected free neighbors around the voxel.
            if ((CanWalk && standingOnGround) || (CanSwim && inWater))
            {
                // If the creature is in water, it can swim. Otherwise, it will walk.
                var moveType = inWater ? MoveType.Swim : MoveType.Walk;
                if (Storage.Neighborhood[0, 1, 1].IsValid && Storage.Neighborhood[0, 1, 1].IsEmpty)
                {
                    // +- x
                    yield return(new MoveAction
                    {
                        SourceState = state,
                        DestinationVoxel = Storage.Neighborhood[0, 1, 1],
                        Diff = new Vector3(0, 1, 1),
                        MoveType = moveType,
                        CostMultiplier = 1.0f
                    });
                }

                if (Storage.Neighborhood[2, 1, 1].IsValid && Storage.Neighborhood[2, 1, 1].IsEmpty)
                {
                    yield return(new MoveAction
                    {
                        SourceState = state,
                        Diff = new Vector3(2, 1, 1),
                        MoveType = moveType,
                        CostMultiplier = 1.0f,
                        DestinationVoxel = Storage.Neighborhood[2, 1, 1]
                    });
                }

                if (Storage.Neighborhood[1, 1, 0].IsValid && Storage.Neighborhood[1, 1, 0].IsEmpty)
                {
                    // +- z
                    yield return(new MoveAction
                    {
                        SourceState = state,
                        Diff = new Vector3(1, 1, 0),
                        MoveType = moveType,
                        CostMultiplier = 1.0f,
                        DestinationVoxel = Storage.Neighborhood[1, 1, 0]
                    });
                }

                if (Storage.Neighborhood[1, 1, 2].IsValid && Storage.Neighborhood[1, 1, 2].IsEmpty)
                {
                    yield return(new MoveAction
                    {
                        SourceState = state,
                        Diff = new Vector3(1, 1, 2),
                        MoveType = moveType,
                        CostMultiplier = 1.0f,
                        DestinationVoxel = Storage.Neighborhood[1, 1, 2]
                    });
                }

                // Only bother worrying about 8-connected movement if there are
                // no full neighbors around the voxel.
                if (!hasNeighbors)
                {
                    if (Storage.Neighborhood[2, 1, 2].IsValid && Storage.Neighborhood[2, 1, 2].IsEmpty)
                    {
                        // +x + z
                        yield return(new MoveAction
                        {
                            SourceState = state,
                            Diff = new Vector3(2, 1, 2),
                            MoveType = moveType,
                            CostMultiplier = 1.0f,
                            DestinationVoxel = Storage.Neighborhood[2, 1, 2]
                        });
                    }

                    if (Storage.Neighborhood[2, 1, 0].IsValid && Storage.Neighborhood[2, 1, 0].IsEmpty)
                    {
                        yield return(new MoveAction
                        {
                            SourceState = state,
                            Diff = new Vector3(2, 1, 0),
                            MoveType = moveType,
                            CostMultiplier = 1.0f,
                            DestinationVoxel = Storage.Neighborhood[2, 1, 0]
                        });
                    }

                    if (Storage.Neighborhood[0, 1, 2].IsValid && Storage.Neighborhood[0, 1, 2].IsEmpty)
                    {
                        // -x -z
                        yield return(new MoveAction
                        {
                            SourceState = state,
                            Diff = new Vector3(0, 1, 2),
                            MoveType = moveType,
                            CostMultiplier = 1.0f,
                            DestinationVoxel = Storage.Neighborhood[0, 1, 2]
                        });
                    }

                    if (Storage.Neighborhood[0, 1, 0].IsValid && Storage.Neighborhood[0, 1, 0].IsEmpty)
                    {
                        yield return(new MoveAction
                        {
                            SourceState = state,
                            Diff = new Vector3(0, 1, 0),
                            MoveType = moveType,
                            CostMultiplier = 1.0f,
                            DestinationVoxel = Storage.Neighborhood[0, 1, 0]
                        });
                    }
                }
            }

            // If the creature's head is free, and it is standing on ground,
            // or if it is in water, or if it is climbing, it can also jump
            // to voxels that are 1 cell away and 1 cell up.
            if (!topCovered && (standingOnGround || (CanSwim && inWater) || isClimbing))
            {
                for (int dx = 0; dx <= 2; dx++)
                {
                    for (int dz = 0; dz <= 2; dz++)
                    {
                        if (dx == 1 && dz == 1)
                        {
                            continue;
                        }

                        if (Storage.Neighborhood[dx, 1, dz].IsValid && !Storage.Neighborhood[dx, 1, dz].IsEmpty)
                        {
                            yield return(new MoveAction
                            {
                                SourceState = state,
                                Diff = new Vector3(dx, 2, dz),
                                MoveType = MoveType.Jump,
                                DestinationVoxel = Storage.Neighborhood[dx, 2, dz],
                                CostMultiplier = 1.0f
                            });
                        }
                    }
                }
            }

            /*
             * if (CanDig)
             * {
             *  // This loop is unrolled for speed. It gets the manhattan neighbors and tells the creature that it can mine
             *  // the surrounding rock to get through.
             *  VoxelHandle neighbor = Storage.Neighborhood[0, 1, 1];
             *  if (neighbor.IsValid && !neighbor.IsEmpty && (!IsDwarf || !neighbor.IsPlayerBuilt))
             *  {
             *      yield return (new MoveAction
             *      {
             *          SourceState = state,
             *          Diff = new Vector3(0, 1, 1),
             *          MoveType = MoveType.Dig,
             *          DestinationVoxel = neighbor,
             *          CostMultiplier = 1.0f
             *      });
             *  }
             *
             *  neighbor = Storage.Neighborhood[2, 1, 1];
             *  if (neighbor.IsValid && !neighbor.IsEmpty && (!IsDwarf || !neighbor.IsPlayerBuilt))
             *  {
             *      yield return (new MoveAction
             *      {
             *          SourceState = state,
             *          Diff = new Vector3(2, 1, 1),
             *          MoveType = MoveType.Dig,
             *          DestinationVoxel = neighbor,
             *          CostMultiplier = 1.0f
             *      });
             *  }
             *
             *  neighbor = Storage.Neighborhood[1, 1, 2];
             *  if (neighbor.IsValid && !neighbor.IsEmpty && (!IsDwarf || !neighbor.IsPlayerBuilt))
             *  {
             *      yield return (new MoveAction
             *      {
             *          SourceState = state,
             *          Diff = new Vector3(1, 1, 2),
             *          MoveType = MoveType.Dig,
             *          DestinationVoxel = neighbor,
             *          CostMultiplier = 1.0f
             *      });
             *  }
             *
             *  neighbor = Storage.Neighborhood[1, 1, 0];
             *  if (neighbor.IsValid && !neighbor.IsEmpty && (!IsDwarf || !neighbor.IsPlayerBuilt))
             *  {
             *      yield return (new MoveAction
             *      {
             *          SourceState = state,
             *          Diff = new Vector3(1, 1, 0),
             *          MoveType = MoveType.Dig,
             *          DestinationVoxel = neighbor,
             *          CostMultiplier = 1.0f
             *      });
             *  }
             *
             *  neighbor = Storage.Neighborhood[1, 2, 1];
             *  if (neighbor.IsValid && !neighbor.IsEmpty && (!IsDwarf || !neighbor.IsPlayerBuilt))
             *  {
             *      yield return (new MoveAction
             *      {
             *          SourceState = state,
             *          Diff = new Vector3(1, 2, 1),
             *          MoveType = MoveType.Dig,
             *          DestinationVoxel = neighbor,
             *          CostMultiplier = 1.0f
             *      });
             *  }
             *
             *  neighbor = Storage.Neighborhood[1, 0, 1];
             *  if (neighbor.IsValid && !neighbor.IsEmpty && (!IsDwarf || !neighbor.IsPlayerBuilt))
             *  {
             *      yield return (new MoveAction
             *      {
             *          SourceState = state,
             *          Diff = new Vector3(1, 0, 1),
             *          MoveType = MoveType.Dig,
             *          DestinationVoxel = neighbor,
             *          CostMultiplier = 1.0f
             *      });
             *  }
             * }
             */
        }
        // Inverts GetMoveActions. So, returns the list of move actions whose target is the current voxel.
        // Very, very slow.
        public IEnumerable <MoveAction> GetInverseMoveActions(MoveState currentstate, OctTreeNode OctTree)
        {
            if (Parent == null)
            {
                yield break;
            }

            var creature = Creature;

            if (creature == null)
            {
                yield break;
            }
            var current = currentstate.Voxel;

            if (Can(MoveType.Teleport))
            {
                var teleportObjects = Parent.Faction.OwnedObjects.Where(obj => obj.Active && obj.Tags.Contains("Teleporter"));
                foreach (var obj in teleportObjects)
                {
                    if ((obj.Position - current.WorldPosition).LengthSquared() > 2)
                    {
                        continue;
                    }

                    for (int dx = -TeleportDistance; dx <= TeleportDistance; dx++)
                    {
                        for (int dz = -TeleportDistance; dz <= TeleportDistance; dz++)
                        {
                            for (int dy = -TeleportDistance; dy <= TeleportDistance; dy++)
                            {
                                if (dx * dx + dy * dy + dz * dz > TeleportDistanceSquared)
                                {
                                    continue;
                                }
                                VoxelHandle teleportNeighbor = new VoxelHandle(Parent.World.ChunkManager.ChunkData, current.Coordinate + new GlobalVoxelOffset(dx, dy, dz));

                                if (teleportNeighbor.IsValid && teleportNeighbor.IsEmpty && !VoxelHelpers.GetNeighbor(teleportNeighbor, new GlobalVoxelOffset(0, -1, 0)).IsEmpty)
                                {
                                    yield return(new MoveAction()
                                    {
                                        InteractObject = obj,
                                        Diff = new Vector3(dx, dx, dz),
                                        SourceVoxel = teleportNeighbor,
                                        DestinationState = currentstate,
                                        MoveType = MoveType.Teleport
                                    });
                                }
                            }
                        }
                    }
                }
            }
            foreach (var v in VoxelHelpers.EnumerateCube(current.Coordinate)
                     .Select(n => new VoxelHandle(current.Chunk.Manager.ChunkData, n))
                     .Where(h => h.IsValid))
            {
                foreach (var a in GetMoveActions(new MoveState()
                {
                    Voxel = v
                }, OctTree).Where(a => a.DestinationState == currentstate))
                {
                    yield return(a);
                }

                if (!Can(MoveType.RideVehicle))
                {
                    continue;
                }
                // Now that dwarfs can ride vehicles, the inverse of the move actions becomes extremely complicated. We must now
                // iterate through all rails intersecting every neighbor and see if we can find a connection from that rail to this one.
                // Further, we must iterate through the entire rail network and enumerate all possible directions in and out of that rail.
                // Yay!
                var bodies = new HashSet <Body>();
                OctTree.EnumerateItems(v.GetBoundingBox(), bodies);
                var rails = bodies.OfType <Rail.RailEntity>().Where(r => r.Active);
                foreach (var rail in rails)
                {
                    if (rail.GetContainingVoxel() != v)
                    {
                        continue;
                    }

                    /*
                     * if (!DwarfGame.IsMainThread)
                     * {
                     *  for (int i = 0; i < 1; i++)
                     *  {
                     *      Drawer3D.DrawBox(rail.GetBoundingBox(), Color.Purple, 0.1f, false);
                     *      System.Threading.Thread.Sleep(1);
                     *  }
                     * }
                     */
                    foreach (var neighborRail in rail.NeighborRails.Select(neighbor => creature.Manager.FindComponent(neighbor.NeighborID) as Rail.RailEntity))
                    {
                        var actions = GetMoveActions(new MoveState()
                        {
                            Voxel = v, VehicleState = new VehicleState()
                            {
                                Rail = rail, PrevRail = neighborRail
                            }
                        }, OctTree);
                        foreach (var a in actions.Where(a => a.DestinationState == currentstate))
                        {
                            yield return(a);

                            /*
                             * if (!DwarfGame.IsMainThread && a.MoveType == MoveType.RideVehicle)
                             * {
                             *  for (int i = 0; i < 10; i++)
                             *  {
                             *      Drawer3D.DrawBox(rail.GetBoundingBox(), Color.Red, 0.1f, false);
                             *      System.Threading.Thread.Sleep(1);
                             *  }
                             * }
                             */
                        }
                    }

                    foreach (var a in GetMoveActions(new MoveState()
                    {
                        Voxel = v, VehicleState = new VehicleState()
                        {
                            Rail = rail, PrevRail = null
                        }
                    }, OctTree).Where(a => a.DestinationState == currentstate))
                    {
                        yield return(a);
                    }
                }
            }
        }
        private IEnumerable <MoveAction> EnumerateSuccessors(MoveState state, VoxelHandle voxel, VoxelHandle[,,] neighborHood, bool inWater, bool standingOnGround, bool topCovered, bool hasNeighbors, bool isRiding, HashSet <Body> neighborObjects)
        {
            bool isClimbing = false;

            if (CanClimb || Can(MoveType.RideVehicle))
            {
                //Climbing ladders.

                var bodies = neighborObjects.Where(o => o.GetBoundingBox().Intersects(voxel.GetBoundingBox()));

                if (!isRiding)
                {
                    var ladder = bodies.FirstOrDefault(component => component.Tags.Contains("Climbable"));

                    // if the creature can climb objects and a ladder is in this voxel,
                    // then add a climb action.
                    if (ladder != null && CanClimb)
                    {
                        yield return(new MoveAction
                        {
                            Diff = new Vector3(1, 2, 1),
                            MoveType = MoveType.Climb,
                            InteractObject = ladder
                        });

                        if (!standingOnGround)
                        {
                            yield return(new MoveAction
                            {
                                Diff = new Vector3(1, 0, 1),
                                MoveType = MoveType.Climb,
                                InteractObject = ladder
                            });
                        }
                        standingOnGround = true;
                    }
                }

                if (!isRiding)
                {
                    var rails = bodies.OfType <Rail.RailEntity>().Where(r => r.Active);

                    if (rails.Count() > 0 && Can(MoveType.RideVehicle))
                    {
                        {
                            foreach (var rail in rails)
                            {
                                if (rail.GetContainingVoxel() != state.Voxel)
                                {
                                    continue;
                                }


                                yield return(new MoveAction()
                                {
                                    SourceState = state,
                                    DestinationState = new MoveState()
                                    {
                                        VehicleState = new VehicleState()
                                        {
                                            Rail = rail
                                        },
                                        Voxel = rail.GetContainingVoxel()
                                    },
                                    MoveType = MoveType.EnterVehicle,
                                    Diff = new Vector3(1, 1, 1)
                                });
                            }
                        }
                    }
                }

                if (Can(MoveType.ExitVehicle) && isRiding)
                {
                    yield return(new MoveAction()
                    {
                        SourceState = state,
                        DestinationState = new MoveState()
                        {
                            VehicleState = new VehicleState(),
                            Voxel = state.Voxel
                        },
                        MoveType = MoveType.ExitVehicle,
                        Diff = new Vector3(1, 1, 1)
                    });
                }

                if (Can(MoveType.RideVehicle) && isRiding)
                {
                    foreach (var neighbor in Rail.RailHelper.EnumerateForwardNetworkConnections(state.VehicleState.PrevRail, state.VehicleState.Rail))
                    {
                        var neighborRail = Creature.Manager.FindComponent(neighbor) as Rail.RailEntity;
                        if (neighborRail == null || !neighborRail.Active)
                        {
                            continue;
                        }

                        yield return(new MoveAction()
                        {
                            SourceState = state,
                            DestinationState = new MoveState()
                            {
                                Voxel = neighborRail.GetContainingVoxel(),
                                VehicleState = new VehicleState()
                                {
                                    Rail = neighborRail,
                                    PrevRail = state.VehicleState.Rail
                                }
                            },
                            MoveType = MoveType.RideVehicle,
                        });
                    }
                }
            }

            // If the creature can climb walls and is not blocked by a voxl above.
            if (!isRiding && CanClimbWalls && !topCovered)
            {
                // This monstrosity is unrolling an inner loop so that we don't have to allocate an array or
                // enumerators.
                var wall = VoxelHandle.InvalidHandle;
                var n211 = neighborHood[2, 1, 1];
                if (n211.IsValid && !n211.IsEmpty)
                {
                    wall = n211;
                }
                else
                {
                    var n011 = neighborHood[0, 1, 1];
                    if (n011.IsValid && !n011.IsEmpty)
                    {
                        wall = n011;
                    }
                    else
                    {
                        var n112 = neighborHood[1, 1, 2];
                        if (n112.IsValid && !n112.IsEmpty)
                        {
                            wall = n112;
                        }
                        else
                        {
                            var n110 = neighborHood[1, 1, 0];
                            if (n110.IsValid && !n110.IsEmpty)
                            {
                                wall = n110;
                            }
                        }
                    }
                }

                if (wall.IsValid)
                {
                    isClimbing = true;
                    yield return(new MoveAction
                    {
                        Diff = new Vector3(1, 2, 1),
                        MoveType = MoveType.ClimbWalls,
                        ActionVoxel = wall
                    });

                    if (!standingOnGround)
                    {
                        yield return(new MoveAction
                        {
                            Diff = new Vector3(1, 0, 1),
                            MoveType = MoveType.ClimbWalls,
                            ActionVoxel = wall
                        });
                    }
                }
            }

            // If the creature either can walk or is in water, add the
            // eight-connected free neighbors around the voxel.
            if (!isRiding && ((CanWalk && standingOnGround) || (CanSwim && inWater)))
            {
                // If the creature is in water, it can swim. Otherwise, it will walk.
                var moveType = inWater ? MoveType.Swim : MoveType.Walk;
                if (!neighborHood[0, 1, 1].IsValid || neighborHood[0, 1, 1].IsEmpty)
                {
                    // +- x
                    yield return(new MoveAction
                    {
                        Diff = new Vector3(0, 1, 1),
                        MoveType = moveType
                    });
                }

                if (!neighborHood[2, 1, 1].IsValid || neighborHood[2, 1, 1].IsEmpty)
                {
                    yield return(new MoveAction
                    {
                        Diff = new Vector3(2, 1, 1),
                        MoveType = moveType
                    });
                }

                if (!neighborHood[1, 1, 0].IsValid || neighborHood[1, 1, 0].IsEmpty)
                {
                    // +- z
                    yield return(new MoveAction
                    {
                        Diff = new Vector3(1, 1, 0),
                        MoveType = moveType
                    });
                }

                if (!neighborHood[1, 1, 2].IsValid || neighborHood[1, 1, 2].IsEmpty)
                {
                    yield return(new MoveAction
                    {
                        Diff = new Vector3(1, 1, 2),
                        MoveType = moveType
                    });
                }

                // Only bother worrying about 8-connected movement if there are
                // no full neighbors around the voxel.
                if (!hasNeighbors)
                {
                    if (!neighborHood[2, 1, 2].IsValid || neighborHood[2, 1, 2].IsEmpty)
                    {
                        // +x + z
                        yield return(new MoveAction
                        {
                            Diff = new Vector3(2, 1, 2),
                            MoveType = moveType
                        });
                    }

                    if (!neighborHood[2, 1, 0].IsValid || neighborHood[2, 1, 0].IsEmpty)
                    {
                        yield return(new MoveAction
                        {
                            Diff = new Vector3(2, 1, 0),
                            MoveType = moveType
                        });
                    }

                    if (!neighborHood[0, 1, 2].IsValid || neighborHood[0, 1, 2].IsEmpty)
                    {
                        // -x -z
                        yield return(new MoveAction
                        {
                            Diff = new Vector3(0, 1, 2),
                            MoveType = moveType
                        });
                    }

                    if (!neighborHood[0, 1, 0].IsValid || neighborHood[0, 1, 0].IsEmpty)
                    {
                        yield return(new MoveAction
                        {
                            Diff = new Vector3(0, 1, 0),
                            MoveType = moveType
                        });
                    }
                }
            }

            // If the creature's head is free, and it is standing on ground,
            // or if it is in water, or if it is climbing, it can also jump
            // to voxels that are 1 cell away and 1 cell up.
            if (!isRiding && (!topCovered && (standingOnGround || (CanSwim && inWater) || isClimbing)))
            {
                for (int dx = 0; dx <= 2; dx++)
                {
                    for (int dz = 0; dz <= 2; dz++)
                    {
                        if (dx == 1 && dz == 1)
                        {
                            continue;
                        }

                        if (neighborHood[dx, 1, dz].IsValid && !neighborHood[dx, 1, dz].IsEmpty)
                        {
                            yield return(new MoveAction
                            {
                                Diff = new Vector3(dx, 2, dz),
                                MoveType = MoveType.Jump
                            });
                        }
                    }
                }
            }

            // If the creature is not in water and is not standing on ground,
            // it can fall one voxel downward in free space.
            if (!isRiding && !inWater && !standingOnGround)
            {
                yield return(new MoveAction
                {
                    Diff = new Vector3(1, 0, 1),
                    MoveType = MoveType.Fall
                });
            }

            // If the creature can fly and is not underwater, it can fly
            // to any adjacent empty cell.
            if (!isRiding && CanFly && !inWater)
            {
                for (int dx = 0; dx <= 2; dx++)
                {
                    for (int dz = 0; dz <= 2; dz++)
                    {
                        for (int dy = 0; dy <= 2; dy++)
                        {
                            if (dx == 1 && dz == 1 && dy == 1)
                            {
                                continue;
                            }

                            if (!neighborHood[dx, 1, dz].IsValid || neighborHood[dx, 1, dz].IsEmpty)
                            {
                                yield return(new MoveAction
                                {
                                    Diff = new Vector3(dx, dy, dz),
                                    MoveType = MoveType.Fly
                                });
                            }
                        }
                    }
                }
            }
            if (!isRiding && CanDig)
            {
                // This loop is unrolled for speed. It gets the manhattan neighbors and tells the creature that it can mine
                // the surrounding rock to get through.
                int         dx       = -1;
                int         dy       = 0;
                int         dz       = 0;
                VoxelHandle neighbor = neighborHood[dx + 1, dy + 1, dz + 1];
                if (neighbor.IsValid && !neighbor.IsEmpty && (!IsDwarf || !neighbor.IsPlayerBuilt))
                {
                    yield return(new MoveAction
                    {
                        Diff = new Vector3(dx + 1, dy + 1, dz + 1),
                        MoveType = MoveType.Dig,
                        DestinationVoxel = neighbor,
                    });
                }

                dx       = 1;
                dy       = 0;
                dz       = 0;
                neighbor = neighborHood[dx + 1, dy + 1, dz + 1];
                if (neighbor.IsValid && !neighbor.IsEmpty && (!IsDwarf || !neighbor.IsPlayerBuilt))
                {
                    yield return(new MoveAction
                    {
                        Diff = new Vector3(dx + 1, dy + 1, dz + 1),
                        MoveType = MoveType.Dig,
                        DestinationVoxel = neighbor,
                    });
                }

                dx       = 0;
                dy       = 0;
                dz       = 1;
                neighbor = neighborHood[dx + 1, dy + 1, dz + 1];
                if (neighbor.IsValid && !neighbor.IsEmpty && (!IsDwarf || !neighbor.IsPlayerBuilt))
                {
                    yield return(new MoveAction
                    {
                        Diff = new Vector3(dx + 1, dy + 1, dz + 1),
                        MoveType = MoveType.Dig,
                        DestinationVoxel = neighbor,
                    });
                }

                dx       = 0;
                dy       = 0;
                dz       = -1;
                neighbor = neighborHood[dx + 1, dy + 1, dz + 1];
                if (neighbor.IsValid && !neighbor.IsEmpty && (!IsDwarf || !neighbor.IsPlayerBuilt))
                {
                    yield return(new MoveAction
                    {
                        Diff = new Vector3(dx + 1, dy + 1, dz + 1),
                        MoveType = MoveType.Dig,
                        DestinationVoxel = neighbor,
                    });
                }

                dx       = 0;
                dy       = 1;
                dz       = 0;
                neighbor = neighborHood[dx + 1, dy + 1, dz + 1];
                if (neighbor.IsValid && !neighbor.IsEmpty && (!IsDwarf || !neighbor.IsPlayerBuilt))
                {
                    yield return(new MoveAction
                    {
                        Diff = new Vector3(dx + 1, dy + 1, dz + 1),
                        MoveType = MoveType.Dig,
                        DestinationVoxel = neighbor,
                    });
                }

                dx       = 0;
                dy       = -1;
                dz       = 0;
                neighbor = neighborHood[dx + 1, dy + 1, dz + 1];
                if (neighbor.IsValid && !neighbor.IsEmpty && (!IsDwarf || !neighbor.IsPlayerBuilt))
                {
                    yield return(new MoveAction
                    {
                        Diff = new Vector3(dx + 1, dy + 1, dz + 1),
                        MoveType = MoveType.Dig,
                        DestinationVoxel = neighbor,
                    });
                }
            }
        }
        /// <summary> gets the list of actions that the creature can take from a given voxel. </summary>
        public IEnumerable <MoveAction> GetMoveActions(MoveState state, OctTreeNode OctTree)
        {
            if (Parent == null)
            {
                yield break;
            }

            var voxel = state.Voxel;

            if (!voxel.IsValid)
            {
                yield break;
            }
            var creature = Creature;

            if (creature == null)
            {
                yield break;
            }

            var  neighborHood     = GetNeighborhood(voxel.Chunk.Manager.ChunkData, voxel);
            bool inWater          = (neighborHood[1, 1, 1].IsValid && neighborHood[1, 1, 1].LiquidLevel > WaterManager.inWaterThreshold);
            bool standingOnGround = (neighborHood[1, 0, 1].IsValid && !neighborHood[1, 0, 1].IsEmpty);
            bool topCovered       = (neighborHood[1, 2, 1].IsValid && !neighborHood[1, 2, 1].IsEmpty);
            bool hasNeighbors     = HasNeighbors(neighborHood);
            bool isRiding         = state.VehicleState.IsRidingVehicle;

            var neighborHoodBounds = new BoundingBox(neighborHood[0, 0, 0].GetBoundingBox().Min, neighborHood[2, 2, 2].GetBoundingBox().Max);
            var neighborObjects    = new HashSet <Body>();

            OctTree.EnumerateItems(neighborHoodBounds, neighborObjects);
            var successors = EnumerateSuccessors(state, voxel, neighborHood, inWater, standingOnGround, topCovered, hasNeighbors, isRiding, neighborObjects);

            if (Can(MoveType.Teleport))
            {
                var teleportObjects = Parent.Faction.OwnedObjects.Where(obj => obj.Active && obj.Tags.Contains("Teleporter"));
                foreach (var obj in teleportObjects)
                {
                    if ((obj.Position - state.Voxel.WorldPosition).LengthSquared() < TeleportDistanceSquared)
                    {
                        yield return(new MoveAction()
                        {
                            InteractObject = obj,
                            MoveType = MoveType.Teleport,
                            SourceVoxel = voxel,
                            DestinationState = new MoveState()
                            {
                                Voxel = new VoxelHandle(voxel.Chunk.Manager.ChunkData, GlobalVoxelCoordinate.FromVector3(obj.Position))
                            }
                        });
                    }
                }
            }
            // Now, validate each move action that the creature might take.
            foreach (MoveAction v in successors)
            {
                var n = v.DestinationVoxel.IsValid ? v.DestinationVoxel : neighborHood[(int)v.Diff.X, (int)v.Diff.Y, (int)v.Diff.Z];
                if (n.IsValid && (v.MoveType == MoveType.Dig || isRiding || n.IsEmpty || n.LiquidLevel > 0))
                {
                    // Do one final check to see if there is an object blocking the motion.
                    bool blockedByObject = false;
                    if (!isRiding)
                    {
                        var objectsAtNeighbor = neighborObjects.Where(o => o.GetBoundingBox().Intersects(n.GetBoundingBox()));

                        // If there is an object blocking the motion, determine if it can be passed through.

                        foreach (var body in objectsAtNeighbor)
                        {
                            var door = body as Door;
                            // ** Doors are in the octtree, pretty sure this was always pointless -- var door = body.GetRoot().EnumerateAll().OfType<Door>().FirstOrDefault();
                            // If there is an enemy door blocking movement, we can destroy it to get through.
                            if (door != null)
                            {
                                if (
                                    creature.World.Diplomacy.GetPolitics(door.TeamFaction, creature.Faction)
                                    .GetCurrentRelationship() ==
                                    Relationship.Hateful)
                                {
                                    if (Can(MoveType.DestroyObject))
                                    {
                                        yield return(new MoveAction
                                        {
                                            Diff = v.Diff,
                                            MoveType = MoveType.DestroyObject,
                                            InteractObject = door,
                                            DestinationVoxel = n,
                                            SourceState = state
                                        });
                                    }
                                    blockedByObject = true;
                                }
                            }
                        }
                    }

                    // If no object blocked us, we can move freely as normal.
                    if (!blockedByObject && n.LiquidType != LiquidType.Lava)
                    {
                        MoveAction newAction = v;
                        newAction.SourceState      = state;
                        newAction.DestinationVoxel = n;
                        yield return(newAction);
                    }
                }
            }
        }
Example #7
0
        // Find a path from the start to the goal by computing an inverse path from goal to the start. Should only be used
        // if the forward path fails.
        private static PlanResult InversePath(CreatureMovement mover, VoxelHandle startVoxel, GoalRegion goal, ChunkManager chunks,
                                              int maxExpansions, ref List <MoveAction> toReturn, float weight, Func <bool> continueFunc)
        {
            // Create a local clone of the octree, using only the objects belonging to the player.
            var         octree          = new OctTreeNode <Body>(mover.Creature.World.ChunkManager.Bounds.Min, mover.Creature.World.ChunkManager.Bounds.Max);
            List <Body> playerObjects   = new List <Body>(mover.Creature.World.PlayerFaction.OwnedObjects);
            List <Body> teleportObjects = mover.Creature.World.PlayerFaction.OwnedObjects.Where(o => o.Tags.Contains("Teleporter")).ToList();

            foreach (var obj in playerObjects)
            {
                octree.Add(obj, obj.GetBoundingBox());
            }

            MoveState start = new MoveState()
            {
                Voxel = startVoxel
            };

            var startTime = DwarfTime.Tick();

            if (mover.IsSessile)
            {
                return(new PlanResult()
                {
                    Result = PlanResultCode.Invalid,
                    Expansions = 0,
                    TimeSeconds = 0
                });
            }

            if (!start.IsValid)
            {
                return(new PlanResult()
                {
                    Result = PlanResultCode.Invalid,
                    Expansions = 0,
                    TimeSeconds = 0
                });
            }


            // Sometimes a goal may not even be achievable a.priori. If this is true, we know there can't be a path
            // which satisifies that goal.
            // It only makes sense to do inverse plans for goals that have an associated voxel.
            if (!goal.IsPossible() || !goal.GetVoxel().IsValid)
            {
                toReturn = null;
                return(new PlanResult()
                {
                    Result = PlanResultCode.Invalid,
                    Expansions = 0,
                    TimeSeconds = 0
                });
            }


            if (goal.IsInGoalRegion(start.Voxel))
            {
                toReturn = new List <MoveAction>();
                return(new PlanResult()
                {
                    Result = PlanResultCode.Success,
                    Expansions = 0,
                    TimeSeconds = 0
                });
            }

            // Voxels that have already been explored.
            var closedSet = new HashSet <MoveState>();

            // Voxels which should be explored.
            var openSet = new HashSet <MoveState>();

            // Dictionary of voxels to the optimal action that got the mover to that voxel.
            var cameFrom = new Dictionary <MoveState, MoveAction>();

            // Optimal score of a voxel based on the path it took to get there.
            var gScore = new Dictionary <MoveState, float>();

            // Expansion priority of voxels.
            var fScore = new PriorityQueue <MoveState>();

            var goalVoxels = new List <VoxelHandle>();

            goalVoxels.Add(goal.GetVoxel());
            // Starting conditions of the search.

            foreach (var goalVoxel in VoxelHelpers.EnumerateCube(goal.GetVoxel().Coordinate)
                     .Select(c => new VoxelHandle(start.Voxel.Chunk.Manager.ChunkData, c)))
            {
                if (!goalVoxel.IsValid)
                {
                    continue;
                }
                var goalState = new MoveState()
                {
                    Voxel = goalVoxel
                };
                if (goal.IsInGoalRegion(goalVoxel))
                {
                    goalVoxels.Add(goalVoxel);
                    openSet.Add(goalState);
                    gScore[goalState] = 0.0f;
                    fScore.Enqueue(goalState,
                                   gScore[goalState] + weight * (goalVoxel.WorldPosition - start.Voxel.WorldPosition).LengthSquared());
                }
            }

            // Keep count of the number of expansions we've taken to get to the goal.
            int numExpansions = 0;

            // Loop until we've either checked every possible voxel, or we've exceeded the maximum number of
            // expansions.
            while (openSet.Count > 0 && numExpansions < maxExpansions)
            {
                if (numExpansions % 10 == 0 && !continueFunc())
                {
                    return new PlanResult
                           {
                               Result      = PlanResultCode.Cancelled,
                               Expansions  = numExpansions,
                               TimeSeconds = DwarfTime.Tock(startTime)
                           }
                }
                ;

                // Check the next voxel to explore.
                var current = GetStateWithMinimumFScore(fScore);
                if (!current.IsValid)
                {
                    continue;
                }
                //Drawer3D.DrawBox(current.GetBoundingBox(), Color.Blue, 0.1f);
                numExpansions++;

                // If we've reached the goal already, reconstruct the path from the start to the
                // goal.
                if (current.Equals(start))
                {
                    toReturn = ReconstructInversePath(goal, cameFrom, cameFrom[start]);
                    return(new PlanResult()
                    {
                        Result = PlanResultCode.Success,
                        Expansions = numExpansions,
                        TimeSeconds = DwarfTime.Tock(startTime)
                    });
                }

                // We've already considered the voxel, so add it to the closed set.
                openSet.Remove(current);
                closedSet.Add(current);

                IEnumerable <MoveAction> neighbors = null;

                // Get the voxels that can be moved to from the current voxel.
                neighbors = mover.GetInverseMoveActions(current, octree, teleportObjects);

                // Otherwise, consider all of the neighbors of the current voxel that can be moved to,
                // and determine how to add them to the list of expansions.
                foreach (MoveAction n in neighbors)
                {
                    //Drawer3D.DrawBox(n.SourceVoxel.GetBoundingBox(), Color.Red, 0.1f);
                    // If we've already explored that voxel, don't explore it again.
                    if (closedSet.Contains(n.SourceState))
                    {
                        continue;
                    }

                    // Otherwise, consider the case of moving to that neighbor.
                    float tenativeGScore = gScore[current] + GetDistance(current.Voxel, n.SourceState.Voxel, n.MoveType, mover);

                    // IF the neighbor can already be reached more efficiently, ignore it.
                    if (openSet.Contains(n.SourceState) && gScore.ContainsKey(n.SourceState) && !(tenativeGScore < gScore[n.SourceState]))
                    {
                        continue;
                    }

                    // Otherwise, add it to the list of voxels for consideration.
                    openSet.Add(n.SourceState);

                    // Add an edge to the voxel from the current voxel.
                    var cameAction = n;
                    cameFrom[n.SourceState] = cameAction;

                    // Update the expansion scores for the next voxel.
                    gScore[n.SourceState] = tenativeGScore;
                    fScore.Enqueue(n.SourceState, gScore[n.SourceState] + weight * ((n.SourceState.Voxel.WorldPosition - start.Voxel.WorldPosition).LengthSquared() + (!n.SourceState.VehicleState.IsRidingVehicle ? 100.0f : 0.0f)));
                }

                // If we've expanded too many voxels, just give up.
                if (numExpansions >= maxExpansions)
                {
                    return(new PlanResult()
                    {
                        Result = PlanResultCode.MaxExpansionsReached,
                        Expansions = numExpansions,
                        TimeSeconds = DwarfTime.Tock(startTime)
                    });
                }
            }

            // Somehow we've reached this code without having found a path. Return false.
            toReturn = null;
            return(new PlanResult()
            {
                Result = PlanResultCode.NoSolution,
                Expansions = numExpansions,
                TimeSeconds = DwarfTime.Tock(startTime)
            });
        }
Example #8
0
        /// <summary>
        ///     Creates a path from the start voxel to some goal region, returning a list of movements that can
        ///     take a mover from the start voxel to the goal region.
        /// </summary>
        /// <param name="mover">The agent that we want to find a path for.</param>
        /// <param name="startVoxel"></param>
        /// <param name="goal">Goal conditions that the agent is trying to satisfy.</param>
        /// <param name="chunks">The voxels that the agent is moving through</param>
        /// <param name="maxExpansions">Maximum number of voxels to consider before giving up.</param>
        /// <param name="toReturn">The path of movements that the mover should take to reach the goal.</param>
        /// <param name="weight">
        ///     A heuristic weight to apply to the planner. If 1.0, the path is garunteed to be optimal. Higher values
        ///     usually result in faster plans that are suboptimal.
        /// </param>
        /// <param name="continueFunc"></param>
        /// <returns>True if a path could be found, or false otherwise.</returns>
        private static PlanResult Path(
            CreatureMovement mover,
            VoxelHandle startVoxel,
            GoalRegion goal,
            ChunkManager chunks,
            int maxExpansions,
            ref List <MoveAction> toReturn,
            float weight,
            Func <bool> continueFunc)
        {
            // Create a local clone of the octree, using only the objects belonging to the player.
            var octree = new OctTreeNode <Body>(mover.Creature.World.ChunkManager.Bounds.Min, mover.Creature.World.ChunkManager.Bounds.Max);

            List <Body> playerObjects   = new List <Body>(mover.Creature.World.PlayerFaction.OwnedObjects);
            List <Body> teleportObjects = playerObjects.Where(o => o.Tags.Contains("Teleporter")).ToList();

            foreach (var obj in playerObjects)
            {
                octree.Add(obj, obj.GetBoundingBox());
            }

            var storage = new MoveActionTempStorage();

            var start = new MoveState()
            {
                Voxel = startVoxel
            };

            var startTime = DwarfTime.Tick();

            if (mover.IsSessile)
            {
                return(new PlanResult()
                {
                    Expansions = 0,
                    Result = PlanResultCode.Invalid,
                    TimeSeconds = DwarfTime.Tock(startTime)
                });
            }

            // Sometimes a goal may not even be achievable a.priori. If this is true, we know there can't be a path
            // which satisifies that goal.
            if (!goal.IsPossible())
            {
                toReturn = null;
                return(new PlanResult()
                {
                    Expansions = 0,
                    Result = PlanResultCode.Invalid,
                    TimeSeconds = DwarfTime.Tock(startTime)
                });
            }

            // Voxels that have already been explored.
            var closedSet = new HashSet <MoveState>();

            // Voxels which should be explored.
            var openSet = new HashSet <MoveState>
            {
                start
            };

            // Dictionary of voxels to the optimal action that got the mover to that voxel.
            var cameFrom = new Dictionary <MoveState, MoveAction>();

            // Optimal score of a voxel based on the path it took to get there.
            var gScore = new Dictionary <MoveState, float>();

            // Expansion priority of voxels.
            var fScore = new PriorityQueue <MoveState>();

            // Starting conditions of the search.
            gScore[start] = 0.0f;
            fScore.Enqueue(start, gScore[start] + weight * goal.Heuristic(start.Voxel));

            // Keep count of the number of expansions we've taken to get to the goal.
            int numExpansions = 0;

            // Check the voxels adjacent to the current voxel as a quick test of adjacency to the goal.
            //var manhattanNeighbors = new List<VoxelHandle>(6);
            //for (int i = 0; i < 6; i++)
            //{
            //    manhattanNeighbors.Add(new VoxelHandle());
            //}

            // Loop until we've either checked every possible voxel, or we've exceeded the maximum number of
            // expansions.
            while (openSet.Count > 0 && numExpansions < maxExpansions)
            {
                if (numExpansions % 10 == 0 && !continueFunc())
                {
                    return new PlanResult
                           {
                               Result      = PlanResultCode.Cancelled,
                               Expansions  = numExpansions,
                               TimeSeconds = DwarfTime.Tock(startTime)
                           }
                }
                ;


                // Check the next voxel to explore.
                var current = GetStateWithMinimumFScore(fScore);
                if (!current.IsValid)
                {
                    // If there wasn't a voxel to explore, just try to expand from
                    // the start again.
                    numExpansions++;

                    continue;
                }

                numExpansions++;

                // If we've reached the goal already, reconstruct the path from the start to the
                // goal.


                if (goal.IsInGoalRegion(current.Voxel) && cameFrom.ContainsKey(current))
                {
                    toReturn = ReconstructPath(cameFrom, cameFrom[current], start);
                    return(new PlanResult()
                    {
                        Result = PlanResultCode.Success,
                        Expansions = numExpansions,
                        TimeSeconds = DwarfTime.Tock(startTime)
                    });
                }

                // We've already considered the voxel, so add it to the closed set.
                openSet.Remove(current);
                closedSet.Add(current);

                // Get the voxels that can be moved to from the current voxel.
                var neighbors = mover.GetMoveActions(current, octree, teleportObjects, storage).ToList();

                var foundGoalAdjacent = neighbors.FirstOrDefault(n => !n.DestinationState.VehicleState.IsRidingVehicle && goal.IsInGoalRegion(n.DestinationState.Voxel));

                // A quick test to see if we're already adjacent to the goal. If we are, assume
                // that we can just walk to it.
                if (foundGoalAdjacent.DestinationState.IsValid && foundGoalAdjacent.SourceState.IsValid)
                {
                    if (cameFrom.ContainsKey(current))
                    {
                        toReturn = ReconstructPath(cameFrom, foundGoalAdjacent, start);
                        return(new PlanResult()
                        {
                            Result = PlanResultCode.Success,
                            Expansions = numExpansions,
                            TimeSeconds = DwarfTime.Tock(startTime)
                        });
                    }
                }

                // Otherwise, consider all of the neighbors of the current voxel that can be moved to,
                // and determine how to add them to the list of expansions.
                foreach (MoveAction n in neighbors)
                {
                    // If we've already explored that voxel, don't explore it again.
                    if (closedSet.Contains(n.DestinationState))
                    {
                        continue;
                    }

                    // Otherwise, consider the case of moving to that neighbor.
                    float tenativeGScore = gScore[current] + GetDistance(current.Voxel, n.DestinationState.Voxel, n.MoveType, mover);

                    // IF the neighbor can already be reached more efficiently, ignore it.
                    if (openSet.Contains(n.DestinationState) && !(tenativeGScore < gScore[n.DestinationState]))
                    {
                        continue;
                    }

                    // Otherwise, add it to the list of voxels for consideration.
                    openSet.Add(n.DestinationState);

                    // Add an edge to the voxel from the current voxel.
                    var cameAction = n;
                    cameFrom[n.DestinationState] = cameAction;

                    // Update the expansion scores for the next voxel.
                    gScore[n.DestinationState] = tenativeGScore;
                    fScore.Enqueue(n.DestinationState, gScore[n.DestinationState] + weight * (goal.Heuristic(n.DestinationState.Voxel) + (!n.DestinationState.VehicleState.IsRidingVehicle ? 100.0f : 0.0f)));
                }

                // If we've expanded too many voxels, just give up.
                if (numExpansions >= maxExpansions)
                {
                    return(new PlanResult()
                    {
                        Expansions = numExpansions,
                        Result = PlanResultCode.MaxExpansionsReached,
                        TimeSeconds = DwarfTime.Tock(startTime)
                    });
                }
            }

            // Somehow we've reached this code without having found a path. Return false.
            toReturn = null;
            return(new PlanResult()
            {
                Expansions = numExpansions,
                Result = PlanResultCode.NoSolution,
                TimeSeconds = DwarfTime.Tock(startTime)
            });
        }