Exemple #1
0
 private void RemoveFromOctTree()
 {
     if (Manager != null)
     {
         Manager.World.OctTree.Remove(this, LastBounds);
     }
     CachedOcttreeNode = null;
 }
        /// <summary> gets a list of actions that the creature can take from the given position </summary>
        public IEnumerable <MoveAction> GetMoveActions(Vector3 Pos, OctTreeNode octree)
        {
            var vox = new VoxelHandle(Creature.World.ChunkManager.ChunkData,
                                      GlobalVoxelCoordinate.FromVector3(Pos));

            return(GetMoveActions(new MoveState()
            {
                Voxel = vox
            }, octree));
        }
Exemple #3
0
        /// <summary> gets a list of actions that the creature can take from the given position </summary>
        public IEnumerable <MoveAction> GetMoveActions(Vector3 Pos, OctTreeNode <Body> octree, List <Body> teleportObjects)
        {
            var vox = new VoxelHandle(Creature.World.ChunkManager.ChunkData,
                                      GlobalVoxelCoordinate.FromVector3(Pos));

            return(GetMoveActions(new MoveState()
            {
                Voxel = vox
            }, octree, teleportObjects, null));
        }
Exemple #4
0
        public void UpdateTransform()
        {
            PerformanceMonitor.PushFrame("Body.UpdateTransform");

            var newTransform = Matrix.Identity;

            if ((Parent as Body) != null)
            {
                newTransform = LocalTransform * (Parent as Body).GlobalTransform;
            }
            else
            {
                newTransform = LocalTransform;
            }

            globalTransform = newTransform;

            UpdateBoundingBox();

            if (CachedOcttreeNode == null || MaxDiff(LastBounds, BoundingBox) > 0.1f)
            {
                //if (CollisionType != CollisionType.None)
                {
                    if (CachedOcttreeNode == null || CachedOcttreeNode.Contains(BoundingBox) == ContainmentType.Disjoint)
                    {
                        RemoveFromOctTree();
                        if (!IsDead)
                        {
                            CachedOcttreeNode = Manager.World.OctTree.Add(this, BoundingBox);
                        }
                    }
                    else
                    {
                        CachedOcttreeNode.Remove(this, LastBounds);
                        if (!IsDead)
                        {
                            CachedOcttreeNode = CachedOcttreeNode.Add(this, BoundingBox);
                        }
                    }
                }

                LastBounds = BoundingBox;
            }

            hasMoved = false;

            PerformanceMonitor.PopFrame();
        }
        // 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 && h.IsEmpty))
            {
                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);
                    }
                }
            }
        }
        /// <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 || !voxel.IsEmpty)
            {
                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 && (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);
                    }
                }
            }
        }
        // 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(mover.Creature.World.ChunkManager.Bounds.Min, mover.Creature.World.ChunkManager.Bounds.Max);
            List <Body> playerObjects = new List <Body>(mover.Creature.World.PlayerFaction.OwnedObjects);

            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);

                // 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)
            });
        }
        /// <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(mover.Creature.World.ChunkManager.Bounds.Min, mover.Creature.World.ChunkManager.Bounds.Max);
            List <Body> playerObjects = new List <Body>(mover.Creature.World.PlayerFaction.OwnedObjects);

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

            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)
                    });
                }


                //Drawer3D.DrawLine(start.Voxel.GetBoundingBox().Center(), goal.GetVoxel().GetBoundingBox().Center(), Color.Red, 0.1f);
                //Drawer3D.DrawBox(current.Voxel.GetBoundingBox(), Color.Red, 0.1f, true);
                // 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.GetMoveActions(current, octree).ToList();
                //currentChunk.GetNeighborsManhattan(current, manhattanNeighbors);


                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))
                    {
                        List <MoveAction> subPath = ReconstructPath(cameFrom, foundGoalAdjacent, start);
                        toReturn = subPath;
                        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)
            });
        }
        private void LoadThreaded()
        {
            DwarfGame.ExitGame = false;
            // Ensure we're using the invariant culture.
            Thread.CurrentThread.CurrentCulture   = CultureInfo.InvariantCulture;
            Thread.CurrentThread.CurrentUICulture = CultureInfo.InvariantCulture;
            LoadStatus = LoadingStatus.Loading;
            SetLoadingMessage("Initializing ...");

            while (GraphicsDevice == null)
            {
                Thread.Sleep(100);
            }
            Thread.Sleep(1000);

#if !DEBUG
            try
            {
#endif
            bool fileExists = !string.IsNullOrEmpty(ExistingFile);

            SetLoadingMessage("Creating Sky...");

            Sky = new SkyRenderer();

            #region Reading game file

            if (fileExists)
            {
                SetLoadingMessage("Loading " + ExistingFile);

                gameFile = SaveGame.CreateFromDirectory(ExistingFile);
                if (gameFile == null)
                {
                    throw new InvalidOperationException("Game File does not exist.");
                }

                // Todo: REMOVE THIS WHEN THE NEW SAVE SYSTEM IS COMPLETE.
                if (gameFile.Metadata.Version != Program.Version && !Program.CompatibleVersions.Contains(gameFile.Metadata.Version))
                {
                    throw new InvalidOperationException(String.Format("Game file is from version {0}. Compatible versions are {1}.", gameFile.Metadata.Version,
                                                                      TextGenerator.GetListString(Program.CompatibleVersions)));
                }

                Sky.TimeOfDay = gameFile.Metadata.TimeOfDay;
                Time          = gameFile.Metadata.Time;
                WorldOrigin   = gameFile.Metadata.WorldOrigin;
                WorldScale    = gameFile.Metadata.WorldScale;
                WorldSize     = gameFile.Metadata.NumChunks;
                GameID        = gameFile.Metadata.GameID;

                if (gameFile.Metadata.OverworldFile != null && gameFile.Metadata.OverworldFile != "flat")
                {
                    SetLoadingMessage("Loading world " + gameFile.Metadata.OverworldFile);
                    Overworld.Name = gameFile.Metadata.OverworldFile;
                    DirectoryInfo worldDirectory =
                        Directory.CreateDirectory(DwarfGame.GetWorldDirectory() +
                                                  Path.DirectorySeparatorChar + Overworld.Name);
                    var overWorldFile = new NewOverworldFile(worldDirectory.FullName);
                    Overworld.Map  = overWorldFile.Data.Data;
                    Overworld.Name = overWorldFile.Data.Name;
                }
                else
                {
                    SetLoadingMessage("Generating flat world..");
                    Overworld.CreateUniformLand(GraphicsDevice);
                }
            }

            #endregion

            #region Initialize static data

            {
                Vector3 origin  = new Vector3(0, 0, 0);
                Vector3 extents = new Vector3(1500, 1500, 1500);
                OctTree = new OctTreeNode <Body>(origin - extents, origin + extents);

                PrimitiveLibrary.Initialize(GraphicsDevice, Content);

                InstanceRenderer = new InstanceRenderer(GraphicsDevice, Content);

                Color[] white = new Color[1];
                white[0] = Color.White;
                pixel    = new Texture2D(GraphicsDevice, 1, 1);
                pixel.SetData(white);

                Tilesheet                  = AssetManager.GetContentTexture(ContentPaths.Terrain.terrain_tiles);
                AspectRatio                = GraphicsDevice.Viewport.AspectRatio;
                DefaultShader              = new Shader(Content.Load <Effect>(ContentPaths.Shaders.TexturedShaders), true);
                DefaultShader.ScreenWidth  = GraphicsDevice.Viewport.Width;
                DefaultShader.ScreenHeight = GraphicsDevice.Viewport.Height;
                CraftLibrary.InitializeDefaultLibrary();
                PotionLibrary.Initialize();
                VoxelLibrary.InitializeDefaultLibrary(GraphicsDevice);
                GrassLibrary.InitializeDefaultLibrary();
                DecalLibrary.InitializeDefaultLibrary();

                bloom = new BloomComponent(Game)
                {
                    Settings = BloomSettings.PresetSettings[5]
                };
                bloom.Initialize();

                SoundManager.Content = Content;
                if (PlanService != null)
                {
                    PlanService.Restart();
                }

                JobLibrary.Initialize();
                MonsterSpawner = new MonsterSpawner(this);
                EntityFactory.Initialize(this);
            }

            #endregion


            SetLoadingMessage("Creating Planner ...");
            PlanService = new PlanService();

            SetLoadingMessage("Creating Shadows...");
            Shadows = new ShadowRenderer(GraphicsDevice, 1024, 1024);

            SetLoadingMessage("Creating Liquids ...");

            #region liquids

            WaterRenderer = new WaterRenderer(GraphicsDevice);

            #endregion

            SetLoadingMessage("Generating Initial Terrain Chunks ...");

            if (!fileExists)
            {
                GameID = MathFunctions.Random.Next(0, 1024);
            }

            ChunkGenerator = new ChunkGenerator(VoxelLibrary, Seed, 0.02f)
            {
                SeaLevel = SeaLevel
            };


            #region Load Components

            if (fileExists)
            {
                ChunkManager = new ChunkManager(Content, this,
                                                ChunkGenerator, WorldSize.X, WorldSize.Y, WorldSize.Z);
                Splasher = new Splasher(ChunkManager);


                ChunkRenderer = new ChunkRenderer(ChunkManager.ChunkData);

                SetLoadingMessage("Loading Terrain...");
                gameFile.ReadChunks(ExistingFile);
                ChunkManager.ChunkData.LoadFromFile(ChunkManager, gameFile, SetLoadingMessage);

                SetLoadingMessage("Loading Entities...");
                gameFile.LoadPlayData(ExistingFile, this);
                Camera            = gameFile.PlayData.Camera;
                DesignationDrawer = gameFile.PlayData.Designations;

                Vector3 origin  = new Vector3(WorldOrigin.X, 0, WorldOrigin.Y);
                Vector3 extents = new Vector3(1500, 1500, 1500);
                if (gameFile.PlayData.Stats != null)
                {
                    Stats = gameFile.PlayData.Stats;
                }
                if (gameFile.PlayData.Resources != null)
                {
                    foreach (var resource in gameFile.PlayData.Resources)
                    {
                        if (!ResourceLibrary.Resources.ContainsKey(resource.Key))
                        {
                            ResourceLibrary.Add(resource.Value);
                        }
                    }
                }
                ComponentManager = new ComponentManager(gameFile.PlayData.Components, this);

                foreach (var component in gameFile.PlayData.Components.SaveableComponents)
                {
                    if (!ComponentManager.HasComponent(component.GlobalID) &&
                        ComponentManager.HasComponent(component.Parent.GlobalID))
                    {
                        // Logically impossible.
                        throw new InvalidOperationException("Component exists in save data but not in manager.");
                    }
                }

                ConversationMemory = gameFile.PlayData.ConversationMemory;

                Factions = gameFile.PlayData.Factions;
                ComponentManager.World = this;

                Sky.TimeOfDay = gameFile.Metadata.TimeOfDay;
                Time          = gameFile.Metadata.Time;
                WorldOrigin   = gameFile.Metadata.WorldOrigin;
                WorldScale    = gameFile.Metadata.WorldScale;

                // Restore native factions from deserialized data.
                Natives = new List <Faction>();

                foreach (Faction faction in Factions.Factions.Values)
                {
                    if (faction.Race.IsNative && faction.Race.IsIntelligent && !faction.IsRaceFaction)
                    {
                        Natives.Add(faction);
                    }
                }

                Diplomacy = gameFile.PlayData.Diplomacy;

                GoalManager = new Goals.GoalManager();
                GoalManager.Initialize(new List <Goals.Goal>());// gameFile.PlayData.Goals);

                TutorialManager = new Tutorial.TutorialManager();
                TutorialManager.SetFromSaveData(gameFile.PlayData.TutorialSaveData);
            }
            else
            {
                Time = new WorldTime();

                Camera = new OrbitCamera(this,
                                         new Vector3(VoxelConstants.ChunkSizeX,
                                                     VoxelConstants.ChunkSizeY - 1.0f,
                                                     VoxelConstants.ChunkSizeZ),
                                         new Vector3(VoxelConstants.ChunkSizeY, VoxelConstants.ChunkSizeY - 1.0f,
                                                     VoxelConstants.ChunkSizeZ) +
                                         Vector3.Up * 10.0f + Vector3.Backward * 10,
                                         MathHelper.PiOver4, AspectRatio, 0.1f,
                                         GameSettings.Default.VertexCullDistance);

                ChunkManager = new ChunkManager(Content, this,
                                                ChunkGenerator, WorldSize.X, WorldSize.Y, WorldSize.Z);
                Splasher = new Splasher(ChunkManager);


                ChunkRenderer = new ChunkRenderer(ChunkManager.ChunkData);

                Camera.Position = new Vector3(0, 10, 0) + new Vector3(WorldSize.X * VoxelConstants.ChunkSizeX, 0, WorldSize.Z * VoxelConstants.ChunkSizeZ) * 0.5f;
                Camera.Target   = new Vector3(0, 10, 1) + new Vector3(WorldSize.X * VoxelConstants.ChunkSizeX, 0, WorldSize.Z * VoxelConstants.ChunkSizeZ) * 0.5f;


                ComponentManager = new ComponentManager(this);
                ComponentManager.SetRootComponent(new Body(ComponentManager, "root", Matrix.Identity, Vector3.Zero, Vector3.Zero));

                if (Natives == null) // Todo: Always true??
                {
                    FactionLibrary library = new FactionLibrary();
                    library.Initialize(this, CompanyMakerState.CompanyInformation);
                    Natives = new List <Faction>();
                    for (int i = 0; i < 10; i++)
                    {
                        Natives.Add(library.GenerateFaction(this, i, 10));
                    }
                }

                #region Prepare Factions

                foreach (Faction faction in Natives)
                {
                    faction.World = this;

                    if (faction.RoomBuilder == null)
                    {
                        faction.RoomBuilder = new RoomBuilder(faction, this);
                    }
                }

                Factions = new FactionLibrary();
                if (Natives != null && Natives.Count > 0)
                {
                    Factions.AddFactions(this, Natives);
                }

                Factions.Initialize(this, CompanyMakerState.CompanyInformation);
                Point playerOrigin = new Point((int)(WorldOrigin.X), (int)(WorldOrigin.Y));

                Factions.Factions["Player"].Center         = playerOrigin;
                Factions.Factions["The Motherland"].Center = new Point(playerOrigin.X + 50, playerOrigin.Y + 50);

                #endregion

                Diplomacy = new Diplomacy(this);
                Diplomacy.Initialize(Time.CurrentDate);

                // Initialize goal manager here.
                GoalManager = new Goals.GoalManager();
                GoalManager.Initialize(new List <Goals.Goal>());

                TutorialManager = new Tutorial.TutorialManager();
                TutorialManager.TutorialEnabled = !GameSettings.Default.TutorialDisabledGlobally;
                Tutorial("new game start");

                foreach (var item in CraftLibrary.EnumerateCraftables())
                {
                    if (!String.IsNullOrEmpty(item.Tutorial))
                    {
                        TutorialManager.AddTutorial(item.Name, item.Tutorial, item.Icon);
                    }
                }
            }

            Camera.World = this;
            //Drawer3D.Camera = Camera;


            #endregion

            SetLoadingMessage("Creating Particles ...");
            ParticleManager = new ParticleManager(ComponentManager);

            SetLoadingMessage("Creating GameMaster ...");
            Master = new GameMaster(Factions.Factions["Player"], Game, ComponentManager, ChunkManager,
                                    Camera, GraphicsDevice);

            // If there's no file, we have to initialize the first chunk coordinate
            if (gameFile == null)
            {
                ChunkManager.GenerateInitialChunks(SpawnRect,
                                                   new GlobalChunkCoordinate(0, 0, 0),
                                                   SetLoadingMessage);
            }

            if (gameFile != null)
            {
                if (gameFile.PlayData.Tasks != null)
                {
                    Master.NewArrivals         = gameFile.PlayData.NewArrivals ?? new List <GameMaster.ApplicantArrival>();
                    Master.TaskManager         = gameFile.PlayData.Tasks;
                    Master.TaskManager.Faction = Master.Faction;
                }
                if (gameFile.PlayData.InitialEmbark != null)
                {
                    InitialEmbark = gameFile.PlayData.InitialEmbark;
                }
                ChunkManager.World.Master.SetMaxViewingLevel(gameFile.Metadata.Slice > 0
                ? gameFile.Metadata.Slice
                : ChunkManager.World.Master.MaxViewingLevel);
            }

            if (Master.Faction.Economy.Company.Information == null)
            {
                Master.Faction.Economy.Company.Information = new CompanyInformation();
            }

            CreateInitialEmbarkment();
            foreach (var chunk in ChunkManager.ChunkData.ChunkMap)
            {
                chunk.CalculateInitialSunlight();
            }

            if (RevealSurface)
            {
                VoxelHelpers.InitialReveal(ChunkManager, ChunkManager.ChunkData, new VoxelHandle(
                                               ChunkManager.ChunkData.GetChunkEnumerator().FirstOrDefault(), new LocalVoxelCoordinate(0, VoxelConstants.ChunkSizeY - 1, 0)));
            }

            foreach (var chunk in ChunkManager.ChunkData.ChunkMap)
            {
                ChunkManager.InvalidateChunk(chunk);
            }

            SetLoadingMessage("Creating Geometry...");
            ChunkManager.GenerateAllGeometry();

            ChunkManager.StartThreads();
            SetLoadingMessage("Presimulating ...");
            ShowingWorld = false;
            OnLoadedEvent();
            Thread.Sleep(1000);

            ShowingWorld = true;

            SetLoadingMessage("Complete.");

            // GameFile is no longer needed.
            gameFile   = null;
            LoadStatus = LoadingStatus.Success;
#if !DEBUG
        }

        catch (Exception exception)
        {
            Game.CaptureException(exception);
            LoadingException = exception;
            LoadStatus       = LoadingStatus.Failure;
            ProgramData.WriteExceptionLog(exception);
        }
#endif
        }
        // 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, List <Body> 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.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
                                    });
                                }
                            }
                        }
                    }
                }
            }

            var storage = new MoveActionTempStorage();

            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, 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?
                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;
                    }

                    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, teleportObjects, storage);
                        foreach (var a in actions.Where(a => a.DestinationState == currentstate))
                        {
                            yield return(a);
                        }
                    }

                    foreach (var a in GetMoveActions(new MoveState()
                    {
                        Voxel = v, VehicleState = new VehicleState()
                        {
                            Rail = rail, PrevRail = null
                        }
                    }, OctTree, teleportObjects, storage).Where(a => a.DestinationState == currentstate))
                    {
                        yield return(a);
                    }
                }
            }
        }
Exemple #11
0
 public NewInstanceManager(GraphicsDevice Device, BoundingBox Bounds, ContentManager Content)
 {
     OctTree  = new OctTreeNode <NewInstanceData>(Bounds.Min, Bounds.Max);
     Renderer = new InstanceRenderer(Device, Content);
 }