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)); }
/// <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)); }
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); } } } }
public NewInstanceManager(GraphicsDevice Device, BoundingBox Bounds, ContentManager Content) { OctTree = new OctTreeNode <NewInstanceData>(Bounds.Min, Bounds.Max); Renderer = new InstanceRenderer(Device, Content); }