diff --git a/CityGame/CityGame.csproj b/CityGame/CityGame.csproj index 2e02cb2..333cf85 100644 --- a/CityGame/CityGame.csproj +++ b/CityGame/CityGame.csproj @@ -10,6 +10,8 @@ + + @@ -86,6 +88,9 @@ PreserveNewest + + Always + PreserveNewest diff --git a/CityGame/Classes/Entities/Car.cs b/CityGame/Classes/Entities/Car.cs index 6df70c5..7a783f0 100644 --- a/CityGame/Classes/Entities/Car.cs +++ b/CityGame/Classes/Entities/Car.cs @@ -3,6 +3,7 @@ using CityGame.Classes.World; using Microsoft.Xna.Framework; using System; using System.Collections.Generic; +using System.Diagnostics.Eventing.Reader; using System.Linq; using System.Runtime.ConstrainedExecution; using System.Windows; @@ -10,27 +11,15 @@ using WPFGame; namespace CityGame.Classes.Entities { - public class PoliceCar : Car - { - public static List Cars = new List(); - public PoliceCar() : base() - { - Cars.Add(this); - Speed = 192; - PNGFile = "PoliceCar.png"; - } - public override void Tick(long deltaTime) - { - - base.Tick(deltaTime); - } - } public class Car : Entity { + public bool desperate = false; + public static List Cars = new List(); public delegate void CarEvent(Car car); public event CarEvent JourneyFinished; public event CarEvent JourneyImpossible; - public ISelectable Target { get; set; } = null; + ISelectable target = null; + public ISelectable Target { get { return target; } set { target = value; Path = null; } } public int NextTarget { get; set; } = 0; public Point[]? Path { get; set; } = null; public Point Point @@ -84,20 +73,27 @@ namespace CityGame.Classes.Entities lights.Add(blight); lights.Add(blight2); - debugRect = new ColoredRectangle(); - MainWindow.GameCanvas.Children.Add(debugRect); - return canvas; } public Car() { + Cars.Add(this); JourneyFinished += c => { }; JourneyImpossible += c => { }; } public override void Tick(long deltaTime) { + //deltaTime *= 500; + Tuple[] fullBlockTiles = new Tuple[] + { + new Tuple(TileType.Road, "4c"), + new Tuple(TileType.Road, "3c"), + new Tuple(TileType.Road, "1"), + new Tuple(TileType.Garage, null) + }; Tile myTile = MainWindow.Grid[Point.X, Point.Y]; + bool fullBlock = fullBlockTiles.Any(x => (x.Item1 == myTile.Type || (x.Item1 == TileType.Road && (myTile.Type == TileType.Path || myTile.Type == TileType.Highway || myTile.Type == TileType.Bridge || myTile.Type == TileType.HighwayBridge))) && (x.Item2 == myTile.Pattern.PatternCode || x.Item2 is null)); if (myTile.Type == TileType.Garage) { Rotation = ((Canvas)myTile.Element).Children[1].Rotation-90; @@ -109,7 +105,9 @@ namespace CityGame.Classes.Entities //if(Object is not null) Object.ToolTip = Target.ToString(); if (Path is null) { - Path = MainWindow.pathfinder.FindPath(Point.Convert(), new Point((int)(Target.X() / MainWindow.TileSize), (int)(Target.Y() / MainWindow.TileSize)).Convert()).Select(x => x.Convert()).ToArray(); + var pf = MainWindow.pathfinder; + if (desperate) pf = MainWindow.pathfinderDesperate; + Path = pf.FindPath(Point.Convert(), new Point((int)(Target.X() / MainWindow.TileSize), (int)(Target.Y() / MainWindow.TileSize)).Convert()).Select(x => x.Convert()).ToArray(); NextTarget = 0; } if (Path.Length == 0) @@ -128,6 +126,16 @@ namespace CityGame.Classes.Entities Path = null; NextTarget = 0; JourneyFinished(this); + if (Math.Round(Rotation) == 0 || Math.Round(Rotation) == 90) + { + if (!OccupiedTilesFill.ContainsKey(myTile)) OccupiedTilesFill.Add(myTile, this); + if (!OccupiedTilesFill2.ContainsKey(myTile) && fullBlock) OccupiedTilesFill2.Add(myTile, this); + } + if (Math.Round(Rotation) == 180 || Math.Round(Rotation) == 270) + { + if (!OccupiedTilesFill2.ContainsKey(myTile)) OccupiedTilesFill2.Add(myTile, this); + if (!OccupiedTilesFill.ContainsKey(myTile) && fullBlock) OccupiedTilesFill.Add(myTile, this); + } return; } if (X.CloselyEquals(nextTarget.X * MainWindow.TileSize) && Y.CloselyEquals(nextTarget.Y * MainWindow.TileSize)) @@ -146,12 +154,14 @@ namespace CityGame.Classes.Entities if (Math.Round(Rotation) == 0 || Math.Round(Rotation) == 90) { if (!OccupiedTilesFill.ContainsKey(myTile)) OccupiedTilesFill.Add(myTile, this); + if (!OccupiedTilesFill2.ContainsKey(myTile) && fullBlock) OccupiedTilesFill2.Add(myTile, this); if (OccupiedTiles.ContainsKey(targetTile) && OccupiedTiles[targetTile] != this) SpeedMulti = 0; } if (Math.Round(Rotation) == 180 || Math.Round(Rotation) == 270) { if (!OccupiedTilesFill2.ContainsKey(myTile)) OccupiedTilesFill2.Add(myTile, this); if (OccupiedTiles2.ContainsKey(targetTile) && OccupiedTiles2[targetTile] != this) SpeedMulti = 0; + if (!OccupiedTilesFill.ContainsKey(myTile) && fullBlock) OccupiedTilesFill.Add(myTile, this); } var possibleDistance = Speed * deltaTime / 1000 * SpeedMulti; @@ -159,6 +169,18 @@ namespace CityGame.Classes.Entities Vector2 travelFinal = direction * finalDistance; X += travelFinal.X; Y += travelFinal.Y; + } else + { + if (Math.Round(Rotation) == 0 || Math.Round(Rotation) == 90) + { + if (!OccupiedTilesFill.ContainsKey(myTile)) OccupiedTilesFill.Add(myTile, this); + if (!OccupiedTilesFill2.ContainsKey(myTile) && fullBlock) OccupiedTilesFill2.Add(myTile, this); + } + if (Math.Round(Rotation) == 180 || Math.Round(Rotation) == 270) + { + if (!OccupiedTilesFill2.ContainsKey(myTile)) OccupiedTilesFill2.Add(myTile, this); + if (!OccupiedTilesFill.ContainsKey(myTile) && fullBlock) OccupiedTilesFill.Add(myTile, this); + } } } } diff --git a/CityGame/Classes/Entities/Helicopter.cs b/CityGame/Classes/Entities/Helicopter.cs index f20a0d6..f64e62d 100644 --- a/CityGame/Classes/Entities/Helicopter.cs +++ b/CityGame/Classes/Entities/Helicopter.cs @@ -1,6 +1,7 @@ using CityGame.Classes.Rendering; using CityGame.Classes.World; using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Audio; using System; using WPFGame; @@ -19,8 +20,17 @@ namespace CityGame.Classes.Entities public ISelectable Target; bool Move; public LightSource Spotlight; + SoundEffectInstance Sound; + AudioEmitter emitter; public override OCanvas Render() { + Sound = Window.GetSound("helicopter").CreateInstance(); + Sound.IsLooped = true; + + + emitter = new AudioEmitter { Position = new Vector3(X, Y, 25) }; + Sound.Apply3D(MainWindow.SoundEffectListener, emitter); + OCanvas canvas = new OCanvas(); Heli1 = new SourcedImage("Helicopter.png"); Heli2 = new SourcedImage("HelicopterFlight.png"); @@ -52,9 +62,11 @@ namespace CityGame.Classes.Entities public override void Tick(long deltaTime) { if (Heli1 is null) return; + emitter.Position = new Vector3(X, Y, 25); Tile myTile = MainWindow.Grid[(int)(X / MainWindow.TileSize), (int)(Y / MainWindow.TileSize)]; if (myTile.Type != TileType.Helipad) { + //if (Sound.State == SoundState.Paused || Sound.State == SoundState.Stopped) Sound.Play(); Spotlight.Visible = true; Heli2.Visible = true; Heli1.Visible = false; @@ -71,6 +83,7 @@ namespace CityGame.Classes.Entities } } else { + if (Sound.State == SoundState.Playing) Sound.Pause(); Spotlight.Visible = false; Blades1.Visible = true; Blades2.Visible = false; diff --git a/CityGame/Classes/Entities/PoliceCar.cs b/CityGame/Classes/Entities/PoliceCar.cs new file mode 100644 index 0000000..106ce23 --- /dev/null +++ b/CityGame/Classes/Entities/PoliceCar.cs @@ -0,0 +1,48 @@ +using CityGame.Classes.Rendering; +using Microsoft.Xna.Framework; +using System; +using System.Collections.Generic; +using WPFGame; + +namespace CityGame.Classes.Entities +{ + public class PoliceCar : Car + { + public static List PCars = new List(); + protected LightSource sirenLight; + public PoliceCar() : base() + { + desperate = true; + PCars.Add(this); + Speed = 192; + PNGFile = "PoliceCar.png"; + } + public override OCanvas Render() + { + OCanvas canvas = base.Render(); + + sirenLight = new LightSource { Radius = 24, Angle = 24, Intensity = 4f, Color = Color.Red, Type = LightSourceType.PointLight, RotationOrigin = new Point(MainWindow.TileSize / 2) }; + canvas.Children.Add(sirenLight); + lights.Add(sirenLight); + Canvas.SetLeft(sirenLight, 43); + Canvas.SetTop(sirenLight, 32); + + return canvas; + } + public override void Tick(long deltaTime) + { + if (sirenLight is null) return; + long ms = DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond; + if (ms / 250 % 2 == 0) + { + sirenLight.Color = Color.Red; + } + else + { + sirenLight.Color = Color.Blue; + } + + base.Tick(deltaTime); + } + } +} \ No newline at end of file diff --git a/CityGame/Classes/Rendering/Pattern.cs b/CityGame/Classes/Rendering/Pattern.cs index 6e08721..ce1eb71 100644 --- a/CityGame/Classes/Rendering/Pattern.cs +++ b/CityGame/Classes/Rendering/Pattern.cs @@ -4,7 +4,7 @@ using CityGame.Classes.World; namespace CityGame.Classes.Rendering { - public class Pattern + public struct Pattern { public string PatternCode { get; set; } public int Rotation { get; set; } diff --git a/CityGame/Classes/Rendering/Renderer.cs b/CityGame/Classes/Rendering/Renderer.cs index ab10677..e838f78 100644 --- a/CityGame/Classes/Rendering/Renderer.cs +++ b/CityGame/Classes/Rendering/Renderer.cs @@ -42,16 +42,20 @@ namespace CityGame.Classes.Rendering if (pattern.PatternCode == "0" && Grid[x, y].Type == TileType.Garage) canvas.Children.Add(new SourcedImage("Garage.png:270")); if (Grid[x,y].Type == TileType.Helipad) canvas.Children.Add(new SourcedImage("Helipad.png")); + Grid[x, y].Pattern = pattern; + return canvas; } if (type == TileType.Lake || type == TileType.River) { Pattern pattern = Pattern.Calculate(Grid, x, y, TileType.Lake, TileType.Bridge, TileType.River, TileType.HighwayBridge); + Grid[x, y].Pattern = pattern; return new SourcedImage("Lake" + pattern.PatternCode + ".png:" + pattern.Rotation, tooltip); } if (type == TileType.Park) { Pattern pattern = Pattern.Calculate(Grid, x, y, TileType.Park, TileType.Path); + Grid[x, y].Pattern = pattern; OCanvas canvas = new SourcedImage("Park" + pattern.PatternCode + ".png:" + pattern.Rotation); if (MainWindow.random.Next(0, 4) == 0) canvas.Children.Add(new SourcedImage("Tree.png:" + MainWindow.random.Next(0, 4) * 90, tooltip)); return canvas; @@ -59,6 +63,7 @@ namespace CityGame.Classes.Rendering if (type == TileType.Road) { Pattern pattern = Pattern.Calculate(Grid, x, y, TileType.Road, TileType.Path, TileType.Bridge, TileType.Highway, TileType.HighwayBridge); + Grid[x, y].Pattern = pattern; if (pattern.PatternCode == "2c") pattern.Rotation += 270; if (pattern.PatternCode == "1") pattern.Rotation += 180; return new SourcedImage("Road" + pattern.PatternCode + ".png:" + pattern.Rotation, tooltip); @@ -66,6 +71,7 @@ namespace CityGame.Classes.Rendering if (type == TileType.Highway) { Pattern pattern = Pattern.Calculate(Grid, x, y, TileType.Road, TileType.Path, TileType.Bridge, TileType.Highway, TileType.HighwayBridge); + Grid[x, y].Pattern = pattern; if (pattern.PatternCode == "2c") pattern.Rotation += 270; if (pattern.PatternCode == "1") pattern.Rotation += 180; return new SourcedImage("Highway" + pattern.PatternCode + ".png:" + pattern.Rotation, tooltip); @@ -74,6 +80,7 @@ namespace CityGame.Classes.Rendering { Pattern roadpattern = Pattern.Calculate(Grid, x, y, TileType.Road, TileType.Path, TileType.Bridge, TileType.Highway, TileType.HighwayBridge); Pattern parkpattern = Pattern.Calculate(Grid, x, y, TileType.Path, TileType.Park); + Grid[x, y].Pattern = roadpattern; if (roadpattern.PatternCode == "2c") roadpattern.Rotation += 270; if (roadpattern.PatternCode == "1") roadpattern.Rotation += 180; Image path = new SourcedImage("Path" + roadpattern.PatternCode + ".png:" + roadpattern.Rotation, tooltip); @@ -88,6 +95,7 @@ namespace CityGame.Classes.Rendering { Pattern roadpattern = Pattern.Calculate(Grid, x, y, TileType.Road, TileType.Bridge, TileType.Path, TileType.Highway, TileType.HighwayBridge); Pattern parkpattern = Pattern.Calculate(Grid, x, y, TileType.Bridge, TileType.Lake, TileType.River, TileType.HighwayBridge); + Grid[x, y].Pattern = roadpattern; if (roadpattern.PatternCode == "2c") roadpattern.Rotation += 270; if (roadpattern.PatternCode == "1") roadpattern.Rotation += 180; Image path = new SourcedImage("Bridge" + roadpattern.PatternCode + ".png:" + roadpattern.Rotation, tooltip); @@ -102,6 +110,7 @@ namespace CityGame.Classes.Rendering { Pattern roadpattern = Pattern.Calculate(Grid, x, y, TileType.Road, TileType.Bridge, TileType.Path, TileType.Highway, TileType.HighwayBridge); Pattern parkpattern = Pattern.Calculate(Grid, x, y, TileType.Bridge, TileType.Lake, TileType.River, TileType.HighwayBridge); + Grid[x, y].Pattern = roadpattern; if (roadpattern.PatternCode == "2c") roadpattern.Rotation += 270; if (roadpattern.PatternCode == "1") roadpattern.Rotation += 180; Image path = new SourcedImage("HighwayBridge" + roadpattern.PatternCode + ".png:" + roadpattern.Rotation, tooltip); diff --git a/CityGame/Classes/World/Tile.cs b/CityGame/Classes/World/Tile.cs index 2e1de78..5102d10 100644 --- a/CityGame/Classes/World/Tile.cs +++ b/CityGame/Classes/World/Tile.cs @@ -7,6 +7,7 @@ namespace CityGame.Classes.World { public int BlockID; public TileType Type; + public Pattern Pattern; public int X; public int Y; public UIElement Element; diff --git a/CityGame/MainWindow.xaml.cs b/CityGame/MainWindow.xaml.cs index 25271c0..da935ed 100644 --- a/CityGame/MainWindow.xaml.cs +++ b/CityGame/MainWindow.xaml.cs @@ -4,6 +4,7 @@ using CityGame.Classes.Entities; using CityGame.Classes.Rendering; using CityGame.Classes.World; using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Audio; using Microsoft.Xna.Framework.Input; using SimplexNoise; using System; @@ -12,6 +13,7 @@ using System.Diagnostics; using System.Globalization; using System.Linq; using System.Speech.Synthesis; +using System.Speech.Synthesis.TtsEngine; using WPFGame; namespace CityGame @@ -61,6 +63,7 @@ namespace CityGame public static bool MouseIsDown = false; public static Vector2 MousePos = new Vector2(0, 0); public static PathFinder pathfinder; + public static PathFinder pathfinderDesperate; public static short[,] pathfindingGrid; public static short[,] pathfindingGridDesperate; public static List npcWalkable = new List(); @@ -75,6 +78,8 @@ namespace CityGame internal static Canvas GameCanvas = new OCanvas(); Canvas CameraCanvas = new OCanvas(); Canvas UICanvas = new OCanvas(); + + public static AudioListener SoundEffectListener; public MainWindow() { AddPenumbra(); @@ -353,7 +358,8 @@ namespace CityGame var worldGrid = new WorldGrid(pathfindingGrid); pathfinder = new PathFinder(worldGrid, new PathFinderOptions { PunishChangeDirection = true, UseDiagonals = false, SearchLimit = int.MaxValue, HeuristicFormula = AStar.Heuristics.HeuristicFormula.Euclidean }); - + var worldGridDesperate = new WorldGrid(pathfindingGridDesperate); + pathfinderDesperate = new PathFinder(worldGridDesperate, new PathFinderOptions { PunishChangeDirection = true, UseDiagonals = false, SearchLimit = int.MaxValue, HeuristicFormula = AStar.Heuristics.HeuristicFormula.Euclidean }); foreach (Image image in SourcedImage.GetObjectsBySourceFile("Helipad.png")) { @@ -393,11 +399,14 @@ namespace CityGame Entities.Add(car); } + SoundEffectListener = new AudioListener { Position = new Vector3(0, 0, 100) }; + Show(); } int swv; protected override Color SkyColor(long SpeedFactor) { + return Color.Black; return base.SkyColor(180); } protected override void Update(GameTime time) @@ -410,6 +419,7 @@ namespace CityGame var diff = newpos - MousePos; diff /= CameraZoom; CameraPosition += diff; + SoundEffectListener.Position = new Vector3(CameraPosition, SoundEffectListener.Position.Z); } MousePos = new Vector2(state.X, state.Y); @@ -425,6 +435,8 @@ namespace CityGame if (delta != 0) { + if (multi > 1) SoundEffectListener.Position = new Vector3(SoundEffectListener.Position.X, SoundEffectListener.Position.Y, SoundEffectListener.Position.Z - 10); + else SoundEffectListener.Position = new Vector3(SoundEffectListener.Position.X, SoundEffectListener.Position.Y, SoundEffectListener.Position.Z + 10); CameraZoom *= multi; } diff --git a/CityGame/Resources/helicopter.mp3 b/CityGame/Resources/helicopter.mp3 new file mode 100644 index 0000000..c726f40 Binary files /dev/null and b/CityGame/Resources/helicopter.mp3 differ