/// <summary> /// Creates a new bullet visualisation. /// </summary> /// <param name="quarter">Town quarter where the bullet is located</param> /// <param name="start">Starting position</param> /// <param name="azimuth">The flight direction</param> /// <param name="range">The flight range</param> /// <param name="startHeight">Starting height</param> /// <param name="endHeight">Ending height</param> public BulletVisualisation(TownQuarter quarter, Vector2 start, float azimuth, float range, float startHeight, float endHeight) { Vector2 end = start.Go(range, azimuth); Vector2 startL = start.Go(Width, azimuth - MathHelper.PiOver2); Vector2 startR = start.Go(Width, azimuth + MathHelper.PiOver2); Vector2 endL = end.Go(Width, azimuth - MathHelper.PiOver2); Vector2 endR = end.Go(Width, azimuth + MathHelper.PiOver2); horizontalPlate = new Plate(quarter, startL.ToVector3(startHeight), startR.ToVector3(startHeight), endL.ToVector3(endHeight), endR.ToVector3(endHeight), Texture, Texture, false); verticalPlate = new Plate(quarter, start.ToVector3(startHeight + Width), start.ToVector3(startHeight - Width), end.ToVector3(endHeight + Width), end.ToVector3(endHeight - Width), Texture, Texture, false); }
/// <summary> /// Updates the town logic. It looks for the quarter the player is located in and for the nearby quarter that should be drawn as well. /// </summary> /// <param name="gameTime">Game time</param> public override void Update(GameTime gameTime) { base.Update(gameTime); Debug.Write("Current quarter", currentQuarter.Name); Vector2 playerPosition = Game.Player.PositionInQuarter.XZToVector2(); Vector2 quarterSize = new Vector2(currentQuarter.BitmapSize.Width * TownQuarter.SquareWidth, currentQuarter.BitmapSize.Height * TownQuarter.SquareWidth); if (gameTime.TotalGameTime - lastQuarterChange > quarterChangeTimeOut && ((playerPosition.X > quarterSize.X || playerPosition.Y > quarterSize.Y) || (playerPosition.X < 0 || playerPosition.Y < 0)) && lastNearestInterfaceIndex >= 0) { //Remove drawed quaeters from drawer ClearDrawer(); //Remove player from old quarter space grid currentQuarter.SpaceGrid.RemoveObject(Game.Player); //Changes current quarter TownQuarterInterface usedInterface = currentQuarter.Interfaces[lastNearestInterfaceIndex]; currentQuarter = usedInterface.OppositeInterface.Quarter; //Moves player into new current quarter float angle = ResolveQuarterAzimuthDelta(usedInterface.SidePosition, usedInterface.OppositeInterface.SidePosition); Vector2 delta = ResolveQuarterPositionDelta(usedInterface); Game.Player.MoveTo( new PositionInTown(currentQuarter, Vector3.Transform(Game.Player.PositionInQuarter, Matrix.CreateTranslation(-delta.ToVector3(0)) * Matrix.CreateRotationY(angle)).XZToVector2() // reverse transform of nearest quarter ), Game.Player.Azimuth - angle ); Game.Drawer.MovePanorama(-delta, -angle); //Assings player to new space grid currentQuarter.SpaceGrid.AddObject(Game.Player); Game.Drawer.ShowMessage(gameTime, String.Format("You've entered {0}.", currentQuarter.Name)); //Restart for drawing lastNearestInterfaceIndex = -1; currentQuarterDrawed = false; lastQuarterChange = gameTime.TotalGameTime; } FillDrawer(); currentQuarter.Update(gameTime, false); }
/// <summary> /// Creates a new plate. /// </summary> /// <param name="homeQuarter">Town quarter where is it located</param> /// <param name="upperLeft">Upper left corner</param> /// <param name="upperRight">Upper right corner</param> /// <param name="lowerLeft">Lower left corner</param> /// <param name="lowerRight">Lower right corner</param> /// <param name="front">Front side texture</param> /// <param name="back">Back side texture</param> /// <param name="enableDefaultLightning">Determines if the default lightning is used</param> public Plate(TownQuarter homeQuarter, Vector3 upperLeft, Vector3 upperRight, Vector3 lowerLeft, Vector3 lowerRight, Texture2D front, Texture2D back, bool enableDefaultLightning) : base(upperLeft.XZToVector2(), upperRight.XZToVector2(), lowerLeft.XZToVector2(), lowerRight.XZToVector2()) { quarter = homeQuarter; ul = upperLeft; ur = upperRight; ll = lowerLeft; lr = lowerRight; this.back = back; this.front = front; graphicsDevice = front.GraphicsDevice; frontVertices = new VertexPositionNormalTexture[4]; backVertices = new VertexPositionNormalTexture[4]; indexes = new short[6]; BuildEffects(enableDefaultLightning); }
/// <summary> /// Creates a new spatial object. /// </summary> /// <param name="model">Model</param> /// <param name="quarter">Town quarter</param> /// <param name="positionInQuarter">Position inside the town quarter</param> /// <param name="azimuth">Azimuth</param> /// <param name="worldTransform">World transform matrix</param> public SpatialObject(Model model, TownQuarter quarter, Vector3 positionInQuarter, double azimuth, Matrix worldTransform) : base(new PositionInTown(quarter, positionInQuarter.XZToVector2()), azimuth, (model == null? Vector2.Zero : model.GetSize(worldTransform).XZToVector2())) { Load(model, new PositionInTown(quarter, positionInQuarter.XZToVector2()), positionInQuarter.Y, azimuth, worldTransform); }
/// <summary> /// Creates and generates the whole town. /// </summary> /// <param name="game">The game</param> /// <param name="loadingFrom">Loading form where the generating progress will be shown</param> public Town(ActionGame game, Loading loadingFrom) : base(game) { EmptyTownQuarterOwner.LoadContent(game.Content); quarters = new TownQuarter[game.Settings.TownQuarterCount]; //Town graph creation loadingFrom.SetLabel("Generating town graph..."); loadingFrom.SetValue(0); int[] degrees = new int[game.Settings.TownQuarterCount]; bool[,] edges = new bool[game.Settings.TownQuarterCount, game.Settings.TownQuarterCount]; // Graph is unoriented (symetric). edges[i, j] can be true only if i<j! for (int i = 0; i < game.Settings.TownQuarterCount - 1; i++) // First is made path through all. Graph has to have only one component. { int j = i + 1; degrees[i]++; degrees[j]++; edges[i, j] = true; } for (int i = 0; i < game.Settings.TownQuarterCount; i++) { loadingFrom.SetValue(100 * i / game.Settings.TownQuarterCount); for (int j = i + 1; j < game.Settings.TownQuarterCount; j++) //graph isn't oriented and reflexion is denied { if (!edges[i, j] && degrees[i] < MaxQuarterDegree && degrees[j] < MaxQuarterDegree) { if (game.Random.NextDouble() < 0.65) { degrees[i]++; degrees[j]++; edges[i, j] = true; } } } } //Quarter creating by degrees loadingFrom.SetLabel("Generating quarters and streets..."); loadingFrom.SetValue(0); for (int i = 0; i < game.Settings.TownQuarterCount; i++) { loadingFrom.SetValue(100 * i / game.Settings.TownQuarterCount); float perimeterLength = MinSideLengthPerInterface * Math.Max(degrees[i], 4); // Even interface isn't needed the side must be there perimeterLength *= (float)game.Random.NextDouble() + 1f; //Minimal length can be doubled float width = (perimeterLength / 2f) * (float)(game.Random.NextDouble() * 0.3 + 0.35); //aspect ratio float height = (perimeterLength / 2f) - width; if (width < MinQuarterSideLength) width = MinQuarterSideLength; if (height < MinQuarterSideLength) height = MinQuarterSideLength; do { try { TownQuarter quarter = new TownQuarter(game, new Vector2(width, height), degrees[i]); quarters[i] = quarter; } catch (NoSpaceForInterfaceException) { float widthIncement = MinSideLengthPerInterface * (float)game.Random.NextDouble(); width += widthIncement; height += MinSideLengthPerInterface - widthIncement; } } while (quarters[i] == null); } //Joining interfaces loadingFrom.SetLabel("Building town..."); loadingFrom.SetValue(0); for (int i = 0; i < game.Settings.TownQuarterCount; i++) { loadingFrom.SetValue(100 * i / game.Settings.TownQuarterCount); for (int j = i + 1; j < game.Settings.TownQuarterCount; j++) { if (edges[i, j]) { TownQuarterInterface ifaceI = (from iface in quarters[i].Interfaces where iface.OppositeInterface == null orderby game.Random.Next() select iface).First(); TownQuarterInterface ifaceJ = (from iface in quarters[j].Interfaces where iface.OppositeInterface == null orderby game.Random.Next() select iface).First(); ifaceI.OppositeInterface = ifaceJ; ifaceJ.OppositeInterface = ifaceI; ifaceI.LeftPathGraphVertex.AddNeighborBothDirection(ifaceJ.RightPathGraphVertex, TownQuarter.SquareWidth); ifaceI.RightPathGraphVertex.AddNeighborBothDirection(ifaceJ.LeftPathGraphVertex, TownQuarter.SquareWidth); } } } foreach (var quarter in quarters) { quarter.BuildInterfaceRoadSigns(); } //Town map base creating { Bitmap mapRaster = new Bitmap(MapImageWidth, MapImageHeight); using (Graphics graphics = Graphics.FromImage(mapRaster)) { graphics.FillRectangle(Brushes.White, 0, 0, mapRaster.Width, mapRaster.Height); float angleJump = MathHelper.TwoPi / Game.Settings.TownQuarterCount; float radius = Math.Min(MapImageWidth, MapImageHeight) / 2f - 20f; PointF center = new PointF(MapImageWidth / 2f, MapImageHeight / 2f); for (int i = 0; i < Game.Settings.TownQuarterCount; i++) { for (int j = i + 1; j < Game.Settings.TownQuarterCount; j++) { if (edges[i, j]) { graphics.DrawLine(Pens.Green, center.X + (float)Math.Cos(i * angleJump) * radius, center.Y + (float)Math.Sin(i * angleJump) * radius, center.X + (float)Math.Cos(j * angleJump) * radius, center.Y + (float)Math.Sin(j * angleJump) * radius ); } } } for (int i = 0; i < Game.Settings.TownQuarterCount; i++) { graphics.FillEllipse(Brushes.Black, center.X + (float)Math.Cos(i * angleJump) * radius - 3.5f, center.Y + (float)Math.Sin(i * angleJump) * radius - 3.5f, 7, 7); graphics.DrawString(quarters[i].Name, new Font("Verdana", 12), Brushes.Black, center.X + (float)Math.Cos(i * angleJump) * radius, center.Y + (float)Math.Sin(i * angleJump) * radius - 16); } } mapImage = mapRaster; } GC.Collect(); //Selecting starting quarter currentQuarter = quarters[0]; }
/// <summary> /// Updates the town logic. It looks for the quarter the player is located in and for the nearby quarter that should be drawn as well. /// </summary> /// <param name="gameTime">Game time</param> public override void Update(GameTime gameTime) { base.Update(gameTime); Debug.Write("Current quarter", currentQuarter.Name); Vector2 playerPosition = Game.Player.PositionInQuarter.XZToVector2(); Vector2 quarterSize = new Vector2(currentQuarter.BitmapSize.Width * TownQuarter.SquareWidth, currentQuarter.BitmapSize.Height * TownQuarter.SquareWidth); if (gameTime.TotalGameTime - lastQuarterChange > quarterChangeTimeOut && ((playerPosition.X > quarterSize.X || playerPosition.Y > quarterSize.Y) || (playerPosition.X < 0 || playerPosition.Y < 0)) && lastNearestInterfaceIndex >= 0) { //Remove drawed quaeters from drawer ClearDrawer(); //Remove player from old quarter space grid currentQuarter.SpaceGrid.RemoveObject(Game.Player); //Changes current quarter TownQuarterInterface usedInterface = currentQuarter.Interfaces[lastNearestInterfaceIndex]; currentQuarter = usedInterface.OppositeInterface.Quarter; //Moves player into new current quarter float angle = ResolveQuarterAzimuthDelta(usedInterface.SidePosition, usedInterface.OppositeInterface.SidePosition); Vector2 delta = ResolveQuarterPositionDelta(usedInterface); Game.Player.MoveTo( new PositionInTown(currentQuarter, Vector3.Transform(Game.Player.PositionInQuarter, Matrix.CreateTranslation(-delta.ToVector3(0)) * Matrix.CreateRotationY(angle)).XZToVector2() // reverse transform of nearest quarter ), Game.Player.Azimuth - angle ); Game.Drawer.MovePanorama(-delta , -angle); //Assings player to new space grid currentQuarter.SpaceGrid.AddObject(Game.Player); Game.Drawer.ShowMessage(gameTime, String.Format("You've entered {0}.", currentQuarter.Name)); //Restart for drawing lastNearestInterfaceIndex = -1; currentQuarterDrawed = false; lastQuarterChange = gameTime.TotalGameTime; } FillDrawer(); currentQuarter.Update(gameTime, false); }
/// <summary> /// Creates a new position in the town /// </summary> /// <param name="quarter">The quarter</param> /// <param name="position">Position inside the quarter</param> public PositionInTown(TownQuarter quarter, Vector2 position) { this.positionInQuarter = position; this.quarter = quarter; }
/// <summary> /// Creates and generates the whole town. /// </summary> /// <param name="game">The game</param> /// <param name="loadingFrom">Loading form where the generating progress will be shown</param> public Town(ActionGame game, Loading loadingFrom) : base(game) { EmptyTownQuarterOwner.LoadContent(game.Content); quarters = new TownQuarter[game.Settings.TownQuarterCount]; //Town graph creation loadingFrom.SetLabel("Generating town graph..."); loadingFrom.SetValue(0); int[] degrees = new int[game.Settings.TownQuarterCount]; bool[,] edges = new bool[game.Settings.TownQuarterCount, game.Settings.TownQuarterCount]; // Graph is unoriented (symetric). edges[i, j] can be true only if i<j! for (int i = 0; i < game.Settings.TownQuarterCount - 1; i++) // First is made path through all. Graph has to have only one component. { int j = i + 1; degrees[i]++; degrees[j]++; edges[i, j] = true; } for (int i = 0; i < game.Settings.TownQuarterCount; i++) { loadingFrom.SetValue(100 * i / game.Settings.TownQuarterCount); for (int j = i + 1; j < game.Settings.TownQuarterCount; j++) //graph isn't oriented and reflexion is denied { if (!edges[i, j] && degrees[i] < MaxQuarterDegree && degrees[j] < MaxQuarterDegree) { if (game.Random.NextDouble() < 0.65) { degrees[i]++; degrees[j]++; edges[i, j] = true; } } } } //Quarter creating by degrees loadingFrom.SetLabel("Generating quarters and streets..."); loadingFrom.SetValue(0); for (int i = 0; i < game.Settings.TownQuarterCount; i++) { loadingFrom.SetValue(100 * i / game.Settings.TownQuarterCount); float perimeterLength = MinSideLengthPerInterface * Math.Max(degrees[i], 4); // Even interface isn't needed the side must be there perimeterLength *= (float)game.Random.NextDouble() + 1f; //Minimal length can be doubled float width = (perimeterLength / 2f) * (float)(game.Random.NextDouble() * 0.3 + 0.35); //aspect ratio float height = (perimeterLength / 2f) - width; if (width < MinQuarterSideLength) { width = MinQuarterSideLength; } if (height < MinQuarterSideLength) { height = MinQuarterSideLength; } do { try { TownQuarter quarter = new TownQuarter(game, new Vector2(width, height), degrees[i]); quarters[i] = quarter; } catch (NoSpaceForInterfaceException) { float widthIncement = MinSideLengthPerInterface * (float)game.Random.NextDouble(); width += widthIncement; height += MinSideLengthPerInterface - widthIncement; } }while (quarters[i] == null); } //Joining interfaces loadingFrom.SetLabel("Building town..."); loadingFrom.SetValue(0); for (int i = 0; i < game.Settings.TownQuarterCount; i++) { loadingFrom.SetValue(100 * i / game.Settings.TownQuarterCount); for (int j = i + 1; j < game.Settings.TownQuarterCount; j++) { if (edges[i, j]) { TownQuarterInterface ifaceI = (from iface in quarters[i].Interfaces where iface.OppositeInterface == null orderby game.Random.Next() select iface).First(); TownQuarterInterface ifaceJ = (from iface in quarters[j].Interfaces where iface.OppositeInterface == null orderby game.Random.Next() select iface).First(); ifaceI.OppositeInterface = ifaceJ; ifaceJ.OppositeInterface = ifaceI; ifaceI.LeftPathGraphVertex.AddNeighborBothDirection(ifaceJ.RightPathGraphVertex, TownQuarter.SquareWidth); ifaceI.RightPathGraphVertex.AddNeighborBothDirection(ifaceJ.LeftPathGraphVertex, TownQuarter.SquareWidth); } } } foreach (var quarter in quarters) { quarter.BuildInterfaceRoadSigns(); } //Town map base creating { Bitmap mapRaster = new Bitmap(MapImageWidth, MapImageHeight); using (Graphics graphics = Graphics.FromImage(mapRaster)) { graphics.FillRectangle(Brushes.White, 0, 0, mapRaster.Width, mapRaster.Height); float angleJump = MathHelper.TwoPi / Game.Settings.TownQuarterCount; float radius = Math.Min(MapImageWidth, MapImageHeight) / 2f - 20f; PointF center = new PointF(MapImageWidth / 2f, MapImageHeight / 2f); for (int i = 0; i < Game.Settings.TownQuarterCount; i++) { for (int j = i + 1; j < Game.Settings.TownQuarterCount; j++) { if (edges[i, j]) { graphics.DrawLine(Pens.Green, center.X + (float)Math.Cos(i * angleJump) * radius, center.Y + (float)Math.Sin(i * angleJump) * radius, center.X + (float)Math.Cos(j * angleJump) * radius, center.Y + (float)Math.Sin(j * angleJump) * radius ); } } } for (int i = 0; i < Game.Settings.TownQuarterCount; i++) { graphics.FillEllipse(Brushes.Black, center.X + (float)Math.Cos(i * angleJump) * radius - 3.5f, center.Y + (float)Math.Sin(i * angleJump) * radius - 3.5f, 7, 7); graphics.DrawString(quarters[i].Name, new Font("Verdana", 12), Brushes.Black, center.X + (float)Math.Cos(i * angleJump) * radius, center.Y + (float)Math.Sin(i * angleJump) * radius - 16); } } mapImage = mapRaster; } GC.Collect(); //Selecting starting quarter currentQuarter = quarters[0]; }
/// <summary> /// Searches for shortest path in the graph. Uses Dijkstra 1-1 form (A*). /// </summary> /// <param name="from">Source vertex</param> /// <param name="to">Target vertex</param> /// <param name="keepInTheSameQuarter">Indicator whether the searching has to use only vertices from the same quarter as the from position is in</param> /// <returns>Set of vertices forming result path</returns> public static IEnumerable <PathGraphVertex> FindShortestPath(PathGraphVertex from, PathGraphVertex to, bool keepInTheSameQuarter) { keepInTheSameQuarter = keepInTheSameQuarter && from.Position.Quarter == to.Position.Quarter; TownQuarter fromQuarter = from.Position.Quarter; LinkedList <PathGraphVertex> resultPath = new LinkedList <PathGraphVertex>(); HashSet <PathGraphVertex> closed = new HashSet <PathGraphVertex>(); Dictionary <PathGraphVertex, PathGraphVertex> cameFrom = new Dictionary <PathGraphVertex, PathGraphVertex>(); Dictionary <PathGraphVertex, float> gScore = new Dictionary <PathGraphVertex, float>(); gScore.Add(from, 0); Dictionary <PathGraphVertex, float> fScore = new Dictionary <PathGraphVertex, float>(); fScore.Add(from, gScore[from] + HeuristicDistance(from, to)); HashSet <PathGraphVertex> open = new HashSet <PathGraphVertex>(); open.Add(from); while (open.Count != 0) { float lowestScore = float.MaxValue; PathGraphVertex lowestVertex = null; foreach (PathGraphVertex openedOne in open) { float score = fScore[openedOne]; if (score < lowestScore) { lowestVertex = openedOne; } } PathGraphVertex current = lowestVertex;//open.OrderBy(x => fScore[x]).First(); if (current == to) { PathGraphVertex t = to; while (t != from) { resultPath.AddFirst(t); t = cameFrom[t]; } resultPath.AddFirst(from); return(resultPath); } open.Remove(current); closed.Add(current); foreach (PathGraphVertex n in current.Neighbors) { if (closed.Contains(n) || (keepInTheSameQuarter && n.Position.Quarter != fromQuarter)) { continue; } else { float tempGSore = gScore[current] + current.GetDistanceToNeighbor(n); if (!open.Contains(n) || tempGSore <= gScore[n]) { cameFrom.SetValue(n, current); gScore.SetValue(n, tempGSore); fScore.SetValue(n, gScore[n] + HeuristicDistance(current, n)); if (!open.Contains(n)) { open.Add(n); } } } } } throw new PathNotFoundException("Source and target vertices aren't in the same component."); }
public People.Human CreateAllyGuard(TownQuarter targetQuarter) { return(null); }