diff --git a/CityGame.sln b/CityGame.sln
new file mode 100644
index 0000000..5109268
--- /dev/null
+++ b/CityGame.sln
@@ -0,0 +1,25 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.3.32929.385
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CityGame", "CityGame\CityGame.csproj", "{5D76B596-FC17-4F47-B5C6-D811DE04F806}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {5D76B596-FC17-4F47-B5C6-D811DE04F806}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {5D76B596-FC17-4F47-B5C6-D811DE04F806}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {5D76B596-FC17-4F47-B5C6-D811DE04F806}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {5D76B596-FC17-4F47-B5C6-D811DE04F806}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {B17891C9-0FBF-46C7-8697-0CD3D795A9FD}
+ EndGlobalSection
+EndGlobal
diff --git a/CityGame/App.xaml b/CityGame/App.xaml
new file mode 100644
index 0000000..51191dd
--- /dev/null
+++ b/CityGame/App.xaml
@@ -0,0 +1,9 @@
+
+
+
+
+
diff --git a/CityGame/App.xaml.cs b/CityGame/App.xaml.cs
new file mode 100644
index 0000000..f5fe8e0
--- /dev/null
+++ b/CityGame/App.xaml.cs
@@ -0,0 +1,17 @@
+using System;
+using System.Collections.Generic;
+using System.Configuration;
+using System.Data;
+using System.Linq;
+using System.Threading.Tasks;
+using System.Windows;
+
+namespace CityGame
+{
+ ///
+ /// Interaction logic for App.xaml
+ ///
+ public partial class App : Application
+ {
+ }
+}
diff --git a/CityGame/AssemblyInfo.cs b/CityGame/AssemblyInfo.cs
new file mode 100644
index 0000000..8b5504e
--- /dev/null
+++ b/CityGame/AssemblyInfo.cs
@@ -0,0 +1,10 @@
+using System.Windows;
+
+[assembly: ThemeInfo(
+ ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
+ //(used if a resource is not found in the page,
+ // or application resource dictionaries)
+ ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
+ //(used if a resource is not found in the page,
+ // app, or any theme specific resource dictionaries)
+)]
diff --git a/CityGame/CityGame.csproj b/CityGame/CityGame.csproj
new file mode 100644
index 0000000..d48a3b7
--- /dev/null
+++ b/CityGame/CityGame.csproj
@@ -0,0 +1,121 @@
+
+
+
+ WinExe
+ net6.0-windows
+ enable
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ True
+ True
+ Resources.resx
+
+
+
+
+
+ ResXFileCodeGenerator
+ Resources.Designer.cs
+
+
+
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+
+
diff --git a/CityGame/ColorConversionMaps.cs b/CityGame/ColorConversionMaps.cs
new file mode 100644
index 0000000..68f95bd
--- /dev/null
+++ b/CityGame/ColorConversionMaps.cs
@@ -0,0 +1,60 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace CityGame
+{
+ public class ColorConversionMaps
+ {
+
+ public static Dictionary HouseToLakeMap = new Dictionary
+ {
+ { "#616161", "#006bb3" },
+ { "#3b3b3b", "#005485" }
+ };
+ public static Dictionary LakeToRiver = new Dictionary
+ {
+ { "#006bb3", "#005ba3" },
+ { "#005485", "#004475" }
+ };
+ public static Dictionary HouseToBuildingDarkMap = new Dictionary
+ {
+ { "#616161", "#3b3b3b" },
+ { "#3b3b3b", "#1b1b1b" }
+ };
+ public static Dictionary HouseToBuildingBlueMap = new Dictionary
+ {
+ { "#616161", "#616181" },
+ { "#3b3b3b", "#3b3b5b" }
+ };
+ public static Dictionary HouseToBuildingRedMap = new Dictionary
+ {
+ { "#616161", "#816161" },
+ { "#3b3b3b", "#5b3b3b" }
+ };
+ public static Dictionary HouseToBuildingGreenMap = new Dictionary
+ {
+ { "#616161", "#618161" },
+ { "#3b3b3b", "#3b5b3b" }
+ };
+ public static Dictionary HouseToParkMap = new Dictionary
+ {
+ { "#616161", "#00a34b" },
+ { "#3b3b3b", "#007534" }
+ };
+ public static Dictionary RoadToPathMap = new Dictionary()
+ {
+ { "#00000000", "#00b36b00" },
+ { "#616161", "#00b36b00" },
+ { "#303030", "#8a5e00" },
+ { "#ffffff", "#8a5e00" }
+ };
+ public static Dictionary RoadToBridgeMap = new Dictionary()
+ {
+ { "#00000000", "#00b36b00" },
+ { "#616161", "#00b36b00" }
+ };
+ }
+}
diff --git a/CityGame/CustomShader.cs b/CityGame/CustomShader.cs
new file mode 100644
index 0000000..451c60b
--- /dev/null
+++ b/CityGame/CustomShader.cs
@@ -0,0 +1,35 @@
+using System;
+using System.IO;
+using System.Windows;
+using System.Windows.Media;
+using System.Windows.Media.Effects;
+
+namespace CityGame
+{
+ public class CustomShader : ShaderEffect
+ {
+ private const string _kshaderAsBase64 = @"AAP///7/HwBDVEFCHAAAAE8AAAAAA///AQAAABwAAAAAAQAASAAAADAAAAADAAAAAQACADgAAAAAAAAAaW5wdXQAq6sEAAwAAQABAAEAAAAAAAAAcHNfM18wAE1pY3Jvc29mdCAoUikgSExTTCBTaGFkZXIgQ29tcGlsZXIgMTAuMQCrUQAABQAAD6AAAIA/AAAAAAAAAAAAAAAAHwAAAgUAAIAAAAOQHwAAAgAAAJAACA+gQgAAAwAAD4AAAOSQAAjkoAEAAAIACAuAAADkgAEAAAIACASAAAAAoP//AAA=";
+ private readonly PixelShader _shader;
+
+ public CustomShader(string shaderName)
+ {
+ _shader = new PixelShader();
+ using (var stream = new MemoryStream(Convert.FromBase64String(_kshaderAsBase64)))
+ {
+ _shader.SetStreamSource(stream);
+ }
+ PixelShader = _shader;
+ UpdateShaderValue(InputProperty);
+ }
+
+ public Brush Input
+ {
+ get { return (Brush)GetValue(InputProperty); }
+ set { SetValue(InputProperty, value); }
+ }
+
+ public static readonly DependencyProperty InputProperty =
+ ShaderEffect.RegisterPixelShaderSamplerProperty("Input", typeof(CustomShader), 0);
+
+ }
+}
\ No newline at end of file
diff --git a/CityGame/ImageConverter.cs b/CityGame/ImageConverter.cs
new file mode 100644
index 0000000..7b030a7
--- /dev/null
+++ b/CityGame/ImageConverter.cs
@@ -0,0 +1,33 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+
+namespace CityGame
+{
+ public class ImageConverter
+ {
+ public static void ChangeColor(string srcFile, string destFile, Dictionary conversions)
+ {
+ destFile = Environment.CurrentDirectory + "\\Resources\\" + destFile + ".png";
+ //if (File.Exists(destFile)) return;
+ srcFile = Environment.CurrentDirectory + "\\Resources\\" + srcFile + ".png";
+ if (!File.Exists(srcFile)) return;
+
+ Dictionary Conversions = conversions.Select(x => new KeyValuePair(System.Drawing.ColorTranslator.FromHtml(x.Key), System.Drawing.ColorTranslator.FromHtml(x.Value))).ToDictionary(x => x.Key, x => x.Value);
+ System.Drawing.Bitmap bmp = (System.Drawing.Bitmap)System.Drawing.Bitmap.FromFile(srcFile);
+ for (int x = 0; x < bmp.Width; x++)
+ {
+ for (int y = 0; y < bmp.Height; y++)
+ {
+ var color = bmp.GetPixel(x, y);
+ if (Conversions.ContainsKey(color))
+ {
+ bmp.SetPixel(x, y, Conversions[color]);
+ }
+ }
+ }
+ bmp.Save(destFile);
+ }
+ }
+}
\ No newline at end of file
diff --git a/CityGame/MainWindow.xaml b/CityGame/MainWindow.xaml
new file mode 100644
index 0000000..0c6d5c3
--- /dev/null
+++ b/CityGame/MainWindow.xaml
@@ -0,0 +1,9 @@
+
+
diff --git a/CityGame/MainWindow.xaml.cs b/CityGame/MainWindow.xaml.cs
new file mode 100644
index 0000000..0971332
--- /dev/null
+++ b/CityGame/MainWindow.xaml.cs
@@ -0,0 +1,507 @@
+using AStar;
+using AStar.Options;
+using SimplexNoise;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.Net.WebSockets;
+using System.Numerics;
+using System.Reflection.Metadata;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Navigation;
+using System.Windows.Shapes;
+using System.Windows.Threading;
+
+namespace CityGame
+{
+ public abstract class Entity
+ {
+ public float X { get; set; }
+ public float Y { get; set; }
+ public float Rotation { get; set; }
+ public long Time { get; set; }
+ public OCanvas Object { get; set; }
+ public abstract OCanvas Render();
+ public abstract void Tick(long deltaTime);
+ }
+ public static class Extensions
+ {
+ public static System.Drawing.Point Convert(this Point point)
+ {
+ return new System.Drawing.Point((int)point.X, (int)point.Y);
+ }
+ public static Point Convert(this System.Drawing.Point point)
+ {
+ return new Point(point.X, point.Y);
+ }
+ public static bool CloselyEquals(this double A, double B)
+ {
+ return Math.Round(A) == Math.Round(B);
+ }
+ public static bool CloselyEquals(this float A, double B)
+ {
+ return Math.Round(A) == Math.Round(B);
+ }
+ }
+ public class Car : Entity
+ {
+ public delegate void CarEvent(Car car);
+ public event CarEvent JourneyFinished;
+ public event CarEvent JourneyImpossible;
+ public Point? Target { get; set; } = null;
+ public int NextTarget { get; set; } = 0;
+ public Point[]? Path { get; set; } = null;
+ public Point Point {
+ get
+ {
+ return new Point((int)X / MainWindow.TileSize, (int)Y / MainWindow.TileSize);
+ }
+ set
+ {
+ X = (float)value.X * MainWindow.TileSize;
+ Y = (float)value.Y * MainWindow.TileSize;
+ }
+ }
+ public float Speed { get; set; } = 256;
+ public override OCanvas Render()
+ {
+ return new SourcedImage("Car.png");
+ }
+
+ public override void Tick(long deltaTime)
+ {
+ if(Target is not null)
+ {
+ if (Path is null)
+ {
+ Path = MainWindow.pathfinder.FindPath(Point.Convert(), ((Point)Target).Convert()).Select(x => x.Convert()).ToArray();
+ NextTarget = 0;
+ }
+ if (Path.Length == 0)
+ {
+ JourneyImpossible(this);
+ return;
+ }
+ Point nextTarget = Path[NextTarget];
+ if (X.CloselyEquals(nextTarget.X * MainWindow.TileSize) && Y.CloselyEquals(nextTarget.Y * MainWindow.TileSize))
+ {
+ NextTarget++;
+ }
+ if(NextTarget == Path.Length)
+ {
+ Path = null;
+ Target = null;
+ NextTarget = 0;
+ JourneyFinished(this);
+ return;
+ }
+ if (X.CloselyEquals(nextTarget.X * MainWindow.TileSize) && Y.CloselyEquals(nextTarget.Y * MainWindow.TileSize))
+ return;
+ Vector2 travel = new Vector2((float)nextTarget.X * 64 - X, (float)nextTarget.Y * 64 - Y);
+ Vector2 direction = Vector2.Normalize(travel);
+ float degrees = (float)(Math.Atan2(direction.Y, direction.X) * (180 / Math.PI)) + 90;
+ Rotation = degrees;
+ var possibleDistance = Speed * deltaTime / 1000;
+ var finalDistance = Math.Min(possibleDistance, travel.Length());
+ Vector2 travelFinal = direction * finalDistance;
+ X += travelFinal.X;
+ Y += travelFinal.Y;
+ }
+ }
+ }
+ ///
+ /// Interaction logic for MainWindow.xaml
+ ///
+ public partial class MainWindow : Window
+ {
+ public static Random random;
+ public static bool MouseIsDown = false;
+ public static Point MousePos = new Point(0, 0);
+ public static PathFinder pathfinder;
+ public static short[,] pathfindingGrid;
+ public static List npcWalkable = new List();
+ public static List Entities { get; set; } = new List();
+ public const int TileSize = 64;
+ public MainWindow()
+ {
+ #region | Texture Conversions |
+ string[] patternCodes = new[] { "0", "1", "2", "2c", "3", "3a", "3ab", "3c", "4", "4m", "4c", "5", "7", "8" };
+
+ foreach (var code in patternCodes)
+ {
+ ImageConverter.ChangeColor("Building" + code, "Lake" + code, ColorConversionMaps.HouseToLakeMap);
+ ImageConverter.ChangeColor("Lake" + code, "River" + code, ColorConversionMaps.LakeToRiver);
+ ImageConverter.ChangeColor("Building" + code, "BuildingDark" + code, ColorConversionMaps.HouseToBuildingDarkMap);
+ ImageConverter.ChangeColor("Building" + code, "BuildingBlue" + code, ColorConversionMaps.HouseToBuildingBlueMap);
+ ImageConverter.ChangeColor("Building" + code, "BuildingRed" + code, ColorConversionMaps.HouseToBuildingRedMap);
+ ImageConverter.ChangeColor("Building" + code, "BuildingGreen" + code, ColorConversionMaps.HouseToBuildingGreenMap);
+ ImageConverter.ChangeColor("Building" + code, "Park" + code, ColorConversionMaps.HouseToParkMap);
+ ImageConverter.ChangeColor("Road" + code, "Path" + code, ColorConversionMaps.RoadToPathMap);
+ ImageConverter.ChangeColor("Road" + code, "Bridge" + code, ColorConversionMaps.RoadToBridgeMap);
+ }
+ ImageConverter.ChangeColor("ParkingLot", "ParkingLotDark", ColorConversionMaps.HouseToBuildingDarkMap);
+ ImageConverter.ChangeColor("ParkingLot", "ParkingLotBlue", ColorConversionMaps.HouseToBuildingBlueMap);
+ ImageConverter.ChangeColor("ParkingLot", "ParkingLotRed", ColorConversionMaps.HouseToBuildingRedMap);
+ ImageConverter.ChangeColor("ParkingLot", "ParkingLotGreen", ColorConversionMaps.HouseToBuildingGreenMap);
+
+ ImageConverter.ChangeColor("Error", "ErrorRed", new Dictionary { { "#000000", "#ff0000" } });
+ #endregion
+
+ InitializeComponent();
+
+ #region | Map Generation |
+ #region | Map Generation Constants |
+ int seed = 1;
+
+ Noise.Seed = seed;
+
+ int mapHeight = 100;
+ int mapWidth = 100;
+
+ float[,] lakeMap = Noise.Calc2D(mapWidth, mapHeight, 0.05f);
+
+ Noise.Seed = seed * 24 + 1;
+ float[,] parkMap = Noise.Calc2D(mapWidth, mapHeight, 0.05f);
+
+ Noise.Seed = Noise.Seed * 24 + 1;
+ float[,] riverMap = Noise.Calc2D(mapWidth, mapHeight, 0.01f);
+
+ mapHeight /= 2;
+ mapWidth /= 2;
+
+ int doubleHeight = mapHeight * 2;
+ int doubleWidth = mapWidth * 2;
+
+ int maxBlockHeight = 5;
+ int maxBlockWidth = 5;
+ int minBlockHeight = 3;
+ int minBlockWidth = 3;
+
+ int NPCCount = 100;
+
+ random = new Random(seed);
+
+ #endregion
+
+ #region | Map Initialization |
+ Tile[,] InitialGrid = new Tile[mapWidth, mapHeight];
+
+ List> coords = new List>();
+ for (int y = 0; y < mapHeight; y++)
+ {
+ for (int x = 0; x < mapWidth; x++)
+ {
+ coords.Add(new Tuple(x, y));
+ }
+ }
+
+ coords = coords.OrderBy(x => random.Next(0, coords.Count)).ToList();
+
+ #endregion
+
+ #region | Main Algorithm |
+
+ int i = 0;
+ foreach (var coord in coords)
+ {
+ int x = coord.Item1;
+ int y = coord.Item2;
+
+ int height = random.Next(minBlockHeight, maxBlockHeight + 1);
+ int width = random.Next(minBlockWidth, maxBlockWidth + 1);
+ int yOffset = random.Next(0, height);
+ int xOffset = random.Next(0, width);
+
+ TileType BlockType = TileType.Skyscraper;
+
+ for (int y2 = y - yOffset; y2 < y - yOffset + height; y2++)
+ {
+ for (int x2 = x - xOffset; x2 < x - xOffset + width; x2++)
+ {
+ if (x2 < 0) continue;
+ if (y2 < 0) continue;
+ if (x2 >= mapWidth) continue;
+ if (y2 >= mapHeight) continue;
+ InitialGrid[x2, y2].BlockID = i;
+ InitialGrid[x2, y2].Type = BlockType;
+ if (lakeMap[x2, y2] > 214) InitialGrid[x2, y2].Type = TileType.Park;
+ if (lakeMap[x2, y2] > 219) InitialGrid[x2, y2].Type = TileType.Lake;
+ if (parkMap[x2, y2] > 214 && parkMap[x2, y2] > lakeMap[x2, y2]) InitialGrid[x2, y2].Type = TileType.Park;
+ if (riverMap[x2, y2] > 128 && riverMap[x2,y2] < 148) InitialGrid[x2, y2].Type = TileType.River;
+
+
+ }
+ }
+
+ i++;
+ }
+
+ #endregion
+
+ #region | Doubling |
+
+ Tile[,] IntermediateGrid = new Tile[doubleWidth, doubleHeight];
+ for (int y = 0; y < mapHeight; y++)
+ {
+ for (int x = 0; x < mapWidth; x++)
+ {
+ IntermediateGrid[x * 2, y * 2] = InitialGrid[x, y];
+ IntermediateGrid[x * 2, y * 2 + 1] = InitialGrid[x, y];
+ IntermediateGrid[x * 2 + 1, y * 2 + 1] = InitialGrid[x, y];
+ IntermediateGrid[x * 2 + 1, y * 2] = InitialGrid[x, y];
+
+ IntermediateGrid[x * 2, y * 2].X = x * 2;
+ IntermediateGrid[x * 2, y * 2].Y = y * 2;
+
+ IntermediateGrid[x * 2, y * 2 + 1].X = x * 2;
+ IntermediateGrid[x * 2, y * 2 + 1].Y = y * 2 + 1;
+
+ IntermediateGrid[x * 2 + 1, y * 2 + 1].X = x * 2 + 1;
+ IntermediateGrid[x * 2 + 1, y * 2 + 1].Y = y * 2 + 1;
+
+ IntermediateGrid[x * 2 + 1, y * 2].X = x * 2 + 1;
+ IntermediateGrid[x * 2 + 1, y * 2].Y = y * 2;
+ }
+ }
+ #endregion
+
+ #region | Roads and Bridges |
+ Dictionary decidedBridges = new Dictionary();
+ pathfindingGrid = new short[doubleWidth, doubleHeight];
+ List bridgeTiles = new List();
+ List roadTiles = new List();
+ for (int y = 0; y < doubleHeight; y++)
+ {
+ for (int x = 0; x < doubleWidth; x++)
+ {
+ TileType changeTo = TileType.Road;
+ int myID = IntermediateGrid[x, y].BlockID;
+ if (IntermediateGrid[x, y].Type == TileType.Lake || IntermediateGrid[x, y].Type == TileType.River)
+ {
+ if (!decidedBridges.ContainsKey(myID))
+ {
+ decidedBridges[myID] = random.Next(0, 2) == 0;
+ }
+ if (decidedBridges[myID])
+ {
+ changeTo = TileType.Bridge;
+ }
+ else
+ {
+ continue;
+ }
+ }
+ if (IntermediateGrid[x, y].Type == TileType.Park) changeTo = TileType.Path;
+ if (x < doubleWidth - 1 && IntermediateGrid[x + 1, y].BlockID != myID) IntermediateGrid[x, y].Type = changeTo;
+ if (y < doubleHeight - 1 && IntermediateGrid[x, y + 1].BlockID != myID) IntermediateGrid[x, y].Type = changeTo;
+ bool walkable = ((int)IntermediateGrid[x, y].Type) / 100 == 4;
+ pathfindingGrid[y, x] = (short)(walkable ? 1 : 0);
+ if (IntermediateGrid[x, y].Type == TileType.Bridge) bridgeTiles.Add(IntermediateGrid[x, y]);
+ if (IntermediateGrid[x, y].Type == TileType.Road) roadTiles.Add(IntermediateGrid[x, y]);
+ if (IntermediateGrid[x, y].Type == TileType.Road || IntermediateGrid[x, y].Type == TileType.Bridge) npcWalkable.Add(IntermediateGrid[x, y]);
+ }
+ }
+
+ #endregion
+
+ #region | Pathfinding |
+
+ var worldGrid = new WorldGrid(pathfindingGrid);
+ pathfinder = new PathFinder(worldGrid, new PathFinderOptions { PunishChangeDirection = true, UseDiagonals = false, SearchLimit = int.MaxValue, HeuristicFormula = AStar.Heuristics.HeuristicFormula.Euclidean });
+
+ //foreach(var tile in bridgeTiles)
+ //{
+ // List sortedRoads = roadTiles.OrderBy(x => Math.Abs(x.X - tile.X) + Math.Abs(x.Y - tile.Y)).ToList();
+ // if(!sortedRoads.Any(x => pathfinder.FindPath(new System.Drawing.Point(x.X, x.Y), new System.Drawing.Point(tile.X, tile.Y)).Length > 0))
+ // {
+ // IntermediateGrid[tile.X, tile.Y].Type = TileType.House;
+ // }
+ // i++;
+ //}
+
+ int mainRoadCount = random.Next(1, 2);
+ for(int m = 0; m < mainRoadCount; m++)
+ {
+ int variant = random.Next(0, 2);
+ Point startPoint = new Point(0, 0);
+ Point endPoint = new Point(0, 0);
+ }
+
+ #endregion
+
+ Tile[,] Grid = IntermediateGrid;
+ //for(int y = 0; y < mapHeight; y++)
+ //{
+ // for(int x = 0; x < mapWidth; x++)
+ // {
+ // Grid[x, y] = IntermediateGrid[x * 2 + 1, y * 2 + 1];
+ // }
+ //}
+
+ mapHeight *= 2;
+ mapWidth *= 2;
+
+ #endregion
+
+ Show();
+
+ #region | Rendering |
+
+ Canvas MainCanvas = new OCanvas();
+ Canvas BGCanvas = new OCanvas();
+ Canvas GameCanvas = new OCanvas();
+ Canvas CameraCanvas = new OCanvas();
+ Canvas UICanvas = new OCanvas();
+
+ Canvas.SetLeft(CameraCanvas, 0);
+ Canvas.SetTop(CameraCanvas, 0);
+
+ RenderOptions.SetEdgeMode(GameCanvas, EdgeMode.Aliased);
+
+ MainCanvas.Children.Add(CameraCanvas);
+ MainCanvas.Children.Add(UICanvas);
+
+ CameraCanvas.Children.Add(BGCanvas);
+ CameraCanvas.Children.Add(GameCanvas);
+
+ MainCanvas.HorizontalAlignment = HorizontalAlignment.Left;
+ MainCanvas.VerticalAlignment = VerticalAlignment.Top;
+
+ Content = MainCanvas;
+
+ int tileSize = TileSize;
+ for (int x = 0; x < mapWidth; x++)
+ {
+ for (int y = 0; y < mapHeight; y++)
+ {
+ Canvas image = Renderer.Render(Grid[x, y].Type, x, y, Grid);
+
+ image.Height = tileSize;
+ image.Width = tileSize;
+
+ RenderOptions.SetBitmapScalingMode(image, BitmapScalingMode.NearestNeighbor);
+
+ Canvas.SetLeft(image, x * tileSize);
+ Canvas.SetTop(image, y * tileSize);
+
+ //image.Opacity = pathfindingGrid[y, x];
+
+ GameCanvas.Children.Add(image);
+ }
+ }
+
+ #endregion
+
+ #region | Controls |
+
+ MainCanvas.MouseDown += (a, b) => MouseIsDown = true;
+ MainCanvas.MouseUp += (a, b) => MouseIsDown = false;
+ MainCanvas.MouseMove += (a, b) =>
+ {
+ if (MouseIsDown)
+ {
+ var newpos = PointToScreen(Mouse.GetPosition(this));
+ var diff = newpos - MousePos;
+ Canvas.SetLeft(CameraCanvas, Canvas.GetLeft(CameraCanvas) + diff.X);
+ Canvas.SetTop(CameraCanvas, Canvas.GetTop(CameraCanvas) + diff.Y);
+ }
+ MousePos = PointToScreen(Mouse.GetPosition(this));
+ };
+
+ ScaleTransform scale = new ScaleTransform(1, 1);
+ CameraCanvas.RenderTransform = scale;
+
+ MainCanvas.MouseWheel += (a, b) =>
+ {
+ float multi = 0.952f;
+ if (b.Delta > 0) multi = 1.05f;
+
+ scale.ScaleX *= multi;
+ scale.ScaleY *= multi;
+
+ if (b.Delta < 0)
+ {
+ scale.ScaleX = Math.Floor(scale.ScaleX * 100) / 100f;
+ scale.ScaleY = Math.Floor(scale.ScaleY * 100) / 100f;
+ }
+ else
+ {
+ scale.ScaleX = Math.Ceiling(scale.ScaleX * 100) / 100f;
+ scale.ScaleY = Math.Ceiling(scale.ScaleY * 100) / 100f;
+ }
+
+ Debug.WriteLine(scale.ScaleX);
+ };
+ #endregion
+
+ #region | Entities |
+
+ DispatcherTimer EntityLoop = new DispatcherTimer { Interval = TimeSpan.FromSeconds(1 / 20) };
+ EntityLoop.Tick += (a, b) =>
+ {
+ long milliseconds = DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond;
+ foreach(Entity entity in Entities)
+ {
+ long delta = milliseconds - entity.Time;
+ entity.Time = milliseconds;
+ entity.Tick(delta);
+
+ if(entity.Object is null)
+ {
+ entity.Object = entity.Render();
+ RenderOptions.SetBitmapScalingMode(entity.Object, BitmapScalingMode.NearestNeighbor);
+ var rt = new RotateTransform(entity.Rotation);
+ rt.CenterX = MainWindow.TileSize / 2;
+ rt.CenterY = MainWindow.TileSize / 2;
+ entity.Object.RenderTransform = rt;
+ GameCanvas.Children.Add(entity.Object);
+ }
+ ((RotateTransform)entity.Object.RenderTransform).Angle = entity.Rotation;
+ Canvas.SetLeft(entity.Object, entity.X);
+ Canvas.SetTop(entity.Object, entity.Y);
+
+ }
+ };
+ EntityLoop.Start();
+
+ #endregion
+
+ for(int n = 0; n < NPCCount; n++)
+ {
+ Car car = new Car();
+ Tile startTile = npcWalkable[random.Next(0, npcWalkable.Count)];
+ Tile targetTile = npcWalkable[random.Next(0, npcWalkable.Count)];
+
+ car.Point = new Point(startTile.X, startTile.Y);
+
+ car.Target = new Point(targetTile.X, targetTile.Y);
+
+ Car.CarEvent reset = car =>
+ {
+ Tile targetTile = npcWalkable[random.Next(0, npcWalkable.Count)];
+ DispatcherTimer delay = new DispatcherTimer { Interval = TimeSpan.FromSeconds(random.Next(1, 5)) };
+ delay.Tick += (a, b) =>
+ {
+ car.Target = new Point(targetTile.X, targetTile.Y);
+ delay.Stop();
+ };
+ delay.Start();
+ };
+
+ car.JourneyFinished += reset;
+ car.JourneyImpossible += reset;
+
+ Entities.Add(car);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/CityGame/OCanvas.cs b/CityGame/OCanvas.cs
new file mode 100644
index 0000000..c4d8ce8
--- /dev/null
+++ b/CityGame/OCanvas.cs
@@ -0,0 +1,19 @@
+using System.Windows.Controls;
+
+namespace CityGame
+{
+ public class OCanvas : Canvas
+ {
+ public static implicit operator OCanvas(Image image)
+ {
+ OCanvas canvas = new OCanvas();
+ canvas.Children.Add(image);
+ return canvas;
+ }
+ public OCanvas() : base()
+ {
+ this.Height = 100;
+ this.Width = 100;
+ }
+ }
+}
\ No newline at end of file
diff --git a/CityGame/Pattern.cs b/CityGame/Pattern.cs
new file mode 100644
index 0000000..64dc2ef
--- /dev/null
+++ b/CityGame/Pattern.cs
@@ -0,0 +1,85 @@
+using System;
+using System.Linq;
+
+namespace CityGame
+{
+ public class Pattern
+ {
+ public string PatternCode { get; set; }
+ public int Rotation { get; set; }
+ public Pattern(string pattern)
+ {
+ PatternCode = pattern;
+ Rotation = 0;
+ }
+ public Pattern(string pattern, int rotation)
+ {
+ PatternCode = pattern;
+ Rotation = rotation;
+ }
+ public Pattern(string pattern, string rotation)
+ {
+ PatternCode = pattern;
+ int.TryParse(rotation, out int rot);
+ Rotation = rot;
+ }
+ public static Pattern Calculate(Tile[,] Grid, int x, int y, params TileType[] allowed)
+ {
+ Func IsAdjacent = (X, Y) =>
+ {
+ if (!Renderer.IsInGrid(Grid, X, Y)) return false;
+ if (Grid[x, y].BlockID != Grid[X, Y].BlockID && Grid[x, y].Type == TileType.Skyscraper) return false;
+ return allowed.Contains(Grid[X, Y].Type);
+ };
+
+ if (IsAdjacent(x - 1, y - 1) && IsAdjacent(x, y - 1) && IsAdjacent(x + 1, y - 1) && IsAdjacent(x - 1, y) && IsAdjacent(x + 1, y) && IsAdjacent(x - 1, y + 1) && IsAdjacent(x, y + 1) && IsAdjacent(x + 1, y + 1)) return new Pattern("8");
+
+ if (IsAdjacent(x, y - 1) && IsAdjacent(x + 1, y - 1) && IsAdjacent(x - 1, y) && IsAdjacent(x + 1, y) && IsAdjacent(x - 1, y + 1) && IsAdjacent(x, y + 1) && IsAdjacent(x + 1, y + 1)) return new Pattern("7");
+ if (IsAdjacent(x - 1, y - 1) && IsAdjacent(x, y - 1) && IsAdjacent(x - 1, y) && IsAdjacent(x + 1, y) && IsAdjacent(x - 1, y + 1) && IsAdjacent(x, y + 1) && IsAdjacent(x + 1, y + 1)) return new Pattern("7", "90");
+ if (IsAdjacent(x - 1, y - 1) && IsAdjacent(x, y - 1) && IsAdjacent(x + 1, y - 1) && IsAdjacent(x - 1, y) && IsAdjacent(x + 1, y) && IsAdjacent(x - 1, y + 1) && IsAdjacent(x, y + 1)) return new Pattern("7", "180");
+ if (IsAdjacent(x - 1, y - 1) && IsAdjacent(x, y - 1) && IsAdjacent(x + 1, y - 1) && IsAdjacent(x - 1, y) && IsAdjacent(x + 1, y) && IsAdjacent(x, y + 1) && IsAdjacent(x + 1, y + 1)) return new Pattern("7", "270");
+
+ if (IsAdjacent(x, y - 1) && IsAdjacent(x + 1, y - 1) && IsAdjacent(x + 1, y) && IsAdjacent(x, y + 1) && IsAdjacent(x + 1, y + 1)) return new Pattern("5");
+ if (IsAdjacent(x, y - 1) && IsAdjacent(x - 1, y - 1) && IsAdjacent(x - 1, y) && IsAdjacent(x, y + 1) && IsAdjacent(x - 1, y + 1)) return new Pattern("5", "180");
+ if (IsAdjacent(x - 1, y - 1) && IsAdjacent(x, y - 1) && IsAdjacent(x + 1, y - 1) && IsAdjacent(x - 1, y) && IsAdjacent(x + 1, y)) return new Pattern("5", "270");
+ if (IsAdjacent(x - 1, y + 1) && IsAdjacent(x, y + 1) && IsAdjacent(x + 1, y + 1) && IsAdjacent(x - 1, y) && IsAdjacent(x + 1, y)) return new Pattern("5", "90");
+
+ if (IsAdjacent(x - 1, y) && IsAdjacent(x, y - 1) && IsAdjacent(x + 1, y - 1) && IsAdjacent(x + 1, y)) return new Pattern("4");
+ if (IsAdjacent(x, y - 1) && IsAdjacent(x + 1, y) && IsAdjacent(x + 1, y + 1) && IsAdjacent(x, y + 1)) return new Pattern("4", "90");
+ if (IsAdjacent(x + 1, y) && IsAdjacent(x - 1, y) && IsAdjacent(x - 1, y + 1) && IsAdjacent(x, y + 1)) return new Pattern("4", "180");
+ if (IsAdjacent(x, y + 1) && IsAdjacent(x - 1, y) && IsAdjacent(x - 1, y - 1) && IsAdjacent(x, y - 1)) return new Pattern("4", "270");
+
+ if (IsAdjacent(x + 1, y) && IsAdjacent(x, y - 1) && IsAdjacent(x - 1, y) && IsAdjacent(x - 1, y - 1)) return new Pattern("4m");
+ if (IsAdjacent(x, y + 1) && IsAdjacent(x + 1, y) && IsAdjacent(x, y - 1) && IsAdjacent(x + 1, y - 1)) return new Pattern("4m", "90");
+ if (IsAdjacent(x - 1, y) && IsAdjacent(x, y + 1) && IsAdjacent(x + 1, y) && IsAdjacent(x + 1, y + 1)) return new Pattern("4m", "180");
+ if (IsAdjacent(x, y - 1) && IsAdjacent(x - 1, y) && IsAdjacent(x, y + 1) && IsAdjacent(x - 1, y + 1)) return new Pattern("4m", "270");
+
+ if (IsAdjacent(x + 1, y) && IsAdjacent(x, y + 1) && IsAdjacent(x - 1, y) && IsAdjacent(x, y - 1)) return new Pattern("4c");
+
+ if (IsAdjacent(x + 1, y) && IsAdjacent(x + 1, y + 1) && IsAdjacent(x, y + 1)) return new Pattern("3");
+ if (IsAdjacent(x - 1, y) && IsAdjacent(x - 1, y + 1) && IsAdjacent(x, y + 1)) return new Pattern("3", "90");
+ if (IsAdjacent(x - 1, y) && IsAdjacent(x - 1, y - 1) && IsAdjacent(x, y - 1)) return new Pattern("3", "180");
+ if (IsAdjacent(x + 1, y) && IsAdjacent(x + 1, y - 1) && IsAdjacent(x, y - 1)) return new Pattern("3", "270");
+
+ if (IsAdjacent(x, y - 1) && IsAdjacent(x, y + 1) && IsAdjacent(x - 1, y)) return new Pattern("3c");
+ if (IsAdjacent(x + 1, y) && IsAdjacent(x, y - 1) && IsAdjacent(x - 1, y)) return new Pattern("3c", "90");
+ if (IsAdjacent(x + 1, y) && IsAdjacent(x, y - 1) && IsAdjacent(x, y + 1)) return new Pattern("3c", "180");
+ if (IsAdjacent(x + 1, y) && IsAdjacent(x, y + 1) && IsAdjacent(x - 1, y)) return new Pattern("3c", "270");
+
+ if (IsAdjacent(x - 1, y) && IsAdjacent(x + 1, y)) return new Pattern("2", "90");
+ if (IsAdjacent(x, y - 1) && IsAdjacent(x, y + 1)) return new Pattern("2");
+
+ if (IsAdjacent(x + 1, y) && IsAdjacent(x, y + 1)) return new Pattern("2c");
+ if (IsAdjacent(x - 1, y) && IsAdjacent(x, y + 1)) return new Pattern("2c", "90");
+ if (IsAdjacent(x - 1, y) && IsAdjacent(x, y - 1)) return new Pattern("2c", "180");
+ if (IsAdjacent(x + 1, y) && IsAdjacent(x, y - 1)) return new Pattern("2c", "270");
+
+ if (IsAdjacent(x + 1, y)) return new Pattern("1");
+ if (IsAdjacent(x - 1, y)) return new Pattern("1", "180");
+ if (IsAdjacent(x, y + 1)) return new Pattern("1", "90");
+ if (IsAdjacent(x, y - 1)) return new Pattern("1", "270");
+
+ return new Pattern("0");
+ }
+ }
+}
\ No newline at end of file
diff --git a/CityGame/Properties/Resources.Designer.cs b/CityGame/Properties/Resources.Designer.cs
new file mode 100644
index 0000000..003deb5
--- /dev/null
+++ b/CityGame/Properties/Resources.Designer.cs
@@ -0,0 +1,63 @@
+//------------------------------------------------------------------------------
+//
+// Dieser Code wurde von einem Tool generiert.
+// Laufzeitversion:4.0.30319.42000
+//
+// Änderungen an dieser Datei können falsches Verhalten verursachen und gehen verloren, wenn
+// der Code erneut generiert wird.
+//
+//------------------------------------------------------------------------------
+
+namespace CityGame.Properties {
+ using System;
+
+
+ ///
+ /// Eine stark typisierte Ressourcenklasse zum Suchen von lokalisierten Zeichenfolgen usw.
+ ///
+ // Diese Klasse wurde von der StronglyTypedResourceBuilder automatisch generiert
+ // -Klasse über ein Tool wie ResGen oder Visual Studio automatisch generiert.
+ // Um einen Member hinzuzufügen oder zu entfernen, bearbeiten Sie die .ResX-Datei und führen dann ResGen
+ // mit der /str-Option erneut aus, oder Sie erstellen Ihr VS-Projekt neu.
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ internal class Resources {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal Resources() {
+ }
+
+ ///
+ /// Gibt die zwischengespeicherte ResourceManager-Instanz zurück, die von dieser Klasse verwendet wird.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Resources.ResourceManager ResourceManager {
+ get {
+ if (object.ReferenceEquals(resourceMan, null)) {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("CityGame.Properties.Resources", typeof(Resources).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ ///
+ /// Überschreibt die CurrentUICulture-Eigenschaft des aktuellen Threads für alle
+ /// Ressourcenzuordnungen, die diese stark typisierte Ressourcenklasse verwenden.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Globalization.CultureInfo Culture {
+ get {
+ return resourceCulture;
+ }
+ set {
+ resourceCulture = value;
+ }
+ }
+ }
+}
diff --git a/CityGame/Properties/Resources.resx b/CityGame/Properties/Resources.resx
new file mode 100644
index 0000000..1af7de1
--- /dev/null
+++ b/CityGame/Properties/Resources.resx
@@ -0,0 +1,120 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
\ No newline at end of file
diff --git a/CityGame/Renderer.cs b/CityGame/Renderer.cs
new file mode 100644
index 0000000..fd03451
--- /dev/null
+++ b/CityGame/Renderer.cs
@@ -0,0 +1,88 @@
+using System.Windows.Controls;
+
+namespace CityGame
+{
+ public class Renderer
+ {
+ public static bool IsRoad(Tile[,] Grid, int x, int y)
+ {
+ return IsInGrid(Grid, x, y) && (Grid[x, y].Type == TileType.Road || Grid[x, y].Type == TileType.Bridge);
+ }
+ public static bool IsInGrid(Tile[,] Grid, int x, int y)
+ {
+ return !(x < 0 || y < 0 || x >= Grid.GetLength(0) || y >= Grid.GetLength(1));
+ }
+ public static bool IsBuilding(Tile[,] Grid, int x, int y)
+ {
+ return IsInGrid(Grid, x, y) && (Grid[x, y].Type == TileType.Skyscraper || Grid[x, y].Type == TileType.House);
+ }
+ public static OCanvas Render(TileType type, int x, int y, Tile[,] Grid)
+ {
+ if (type == TileType.Skyscraper)
+ {
+ string theme = "";
+ if (Grid[x, y].BlockID % 2 == 1) theme = "Dark";
+ if (Grid[x, y].BlockID % 30 == 1) theme = "Blue";
+ if (Grid[x, y].BlockID % 30 == 2) theme = "Red";
+ if (Grid[x, y].BlockID % 30 == 3) theme = "Green";
+ Pattern pattern = Pattern.Calculate(Grid, x, y, TileType.Skyscraper);
+ if (pattern.PatternCode == "1" && MainWindow.random.Next(0, 3) == 0) return new SourcedImage("ParkingLot"+theme+".png:" + pattern.Rotation);
+ if (pattern.PatternCode == "3" && MainWindow.random.Next(0, 12) == 0) pattern.PatternCode = "3a";
+ if (pattern.PatternCode == "3" && MainWindow.random.Next(0, 12) == 1) pattern.PatternCode = "3ab";
+ OCanvas canvas = new SourcedImage("Building"+ theme + pattern.PatternCode + ".png:" + pattern.Rotation);
+
+ if (MainWindow.random.Next(0, 10) == 0 && pattern.PatternCode != "3a") canvas.Children.Add(new SourcedImage("Vent" + (MainWindow.random.Next(0, 3) + 1) + ".png:" + (MainWindow.random.Next(0, 4) * 90)));
+
+ return canvas;
+ }
+ if (type == TileType.Lake || type == TileType.River)
+ {
+ Pattern pattern = Pattern.Calculate(Grid, x, y, TileType.Lake, TileType.Bridge, TileType.River);
+ return new SourcedImage("Lake" + pattern.PatternCode + ".png:" + pattern.Rotation);
+ }
+ if (type == TileType.Park)
+ {
+ Pattern pattern = Pattern.Calculate(Grid, x, y, TileType.Park, TileType.Path);
+ 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));
+ return canvas;
+ }
+ if (type == TileType.Road)
+ {
+ Pattern pattern = Pattern.Calculate(Grid, x, y, TileType.Road, TileType.Path, TileType.Bridge);
+ if (pattern.PatternCode == "2c") pattern.Rotation += 270;
+ if (pattern.PatternCode == "1") pattern.Rotation += 180;
+ return new SourcedImage("Road" + pattern.PatternCode + ".png:" + pattern.Rotation);
+ }
+ if (type == TileType.Path)
+ {
+ Pattern roadpattern = Pattern.Calculate(Grid, x, y, TileType.Road, TileType.Path, TileType.Bridge);
+ Pattern parkpattern = Pattern.Calculate(Grid, x, y, TileType.Path, TileType.Park);
+ if (roadpattern.PatternCode == "2c") roadpattern.Rotation += 270;
+ if (roadpattern.PatternCode == "1") roadpattern.Rotation += 180;
+ Image path = new SourcedImage("Path" + roadpattern.PatternCode + ".png:" + roadpattern.Rotation);
+ Image park = new SourcedImage("Park" + parkpattern.PatternCode + ".png:" + parkpattern.Rotation);
+
+ OCanvas result = new OCanvas();
+ result.Children.Add(park);
+ result.Children.Add(path);
+ return result;
+ }
+ if (type == TileType.Bridge)
+ {
+ Pattern roadpattern = Pattern.Calculate(Grid, x, y, TileType.Road, TileType.Bridge, TileType.Path);
+ Pattern parkpattern = Pattern.Calculate(Grid, x, y, TileType.Bridge, TileType.Lake, TileType.River);
+ if (roadpattern.PatternCode == "2c") roadpattern.Rotation += 270;
+ if (roadpattern.PatternCode == "1") roadpattern.Rotation += 180;
+ Image path = new SourcedImage("Bridge" + roadpattern.PatternCode + ".png:" + roadpattern.Rotation);
+ Image park = new SourcedImage("Lake" + parkpattern.PatternCode + ".png:" + parkpattern.Rotation);
+
+ OCanvas result = new OCanvas();
+ result.Children.Add(park);
+ result.Children.Add(path);
+ return result;
+ }
+ return new SourcedImage("Error.png");
+ }
+ }
+}
\ No newline at end of file
diff --git a/CityGame/Resources/BlueTint.hlsl b/CityGame/Resources/BlueTint.hlsl
new file mode 100644
index 0000000..b356f0d
--- /dev/null
+++ b/CityGame/Resources/BlueTint.hlsl
@@ -0,0 +1,9 @@
+Texture2D inputTexture : register(t0);
+SamplerState inputSampler : register(s0);
+
+float4 main(float2 uv : TEXCOORD) : SV_Target
+{
+ float4 color = inputTexture.Sample(inputSampler, uv);
+ float4 grayscaleColor = float4(dot(color.rgb, float3(0.299, 0.587, 0.114)), dot(color.rgb, float3(0.299, 0.587, 0.114)), dot(color.rgb, float3(0.299, 0.587, 0.114)), color.a);
+ return grayscaleColor;
+}
\ No newline at end of file
diff --git a/CityGame/Resources/Building0.png b/CityGame/Resources/Building0.png
new file mode 100644
index 0000000..f3fc232
Binary files /dev/null and b/CityGame/Resources/Building0.png differ
diff --git a/CityGame/Resources/Building1.png b/CityGame/Resources/Building1.png
new file mode 100644
index 0000000..a7de148
Binary files /dev/null and b/CityGame/Resources/Building1.png differ
diff --git a/CityGame/Resources/Building2.png b/CityGame/Resources/Building2.png
new file mode 100644
index 0000000..c3af0ff
Binary files /dev/null and b/CityGame/Resources/Building2.png differ
diff --git a/CityGame/Resources/Building2c.png b/CityGame/Resources/Building2c.png
new file mode 100644
index 0000000..e346aa1
Binary files /dev/null and b/CityGame/Resources/Building2c.png differ
diff --git a/CityGame/Resources/Building3.png b/CityGame/Resources/Building3.png
new file mode 100644
index 0000000..af7008c
Binary files /dev/null and b/CityGame/Resources/Building3.png differ
diff --git a/CityGame/Resources/Building3a.png b/CityGame/Resources/Building3a.png
new file mode 100644
index 0000000..e719927
Binary files /dev/null and b/CityGame/Resources/Building3a.png differ
diff --git a/CityGame/Resources/Building3ab.png b/CityGame/Resources/Building3ab.png
new file mode 100644
index 0000000..291a3b5
Binary files /dev/null and b/CityGame/Resources/Building3ab.png differ
diff --git a/CityGame/Resources/Building3c.png b/CityGame/Resources/Building3c.png
new file mode 100644
index 0000000..be8d3d8
Binary files /dev/null and b/CityGame/Resources/Building3c.png differ
diff --git a/CityGame/Resources/Building4.png b/CityGame/Resources/Building4.png
new file mode 100644
index 0000000..bff9a7e
Binary files /dev/null and b/CityGame/Resources/Building4.png differ
diff --git a/CityGame/Resources/Building4c.png b/CityGame/Resources/Building4c.png
new file mode 100644
index 0000000..595fee5
Binary files /dev/null and b/CityGame/Resources/Building4c.png differ
diff --git a/CityGame/Resources/Building4m.png b/CityGame/Resources/Building4m.png
new file mode 100644
index 0000000..0b5bc21
Binary files /dev/null and b/CityGame/Resources/Building4m.png differ
diff --git a/CityGame/Resources/Building5.png b/CityGame/Resources/Building5.png
new file mode 100644
index 0000000..9ed0867
Binary files /dev/null and b/CityGame/Resources/Building5.png differ
diff --git a/CityGame/Resources/Building7.png b/CityGame/Resources/Building7.png
new file mode 100644
index 0000000..271a4b9
Binary files /dev/null and b/CityGame/Resources/Building7.png differ
diff --git a/CityGame/Resources/Building8.png b/CityGame/Resources/Building8.png
new file mode 100644
index 0000000..c78fea9
Binary files /dev/null and b/CityGame/Resources/Building8.png differ
diff --git a/CityGame/Resources/Car.png b/CityGame/Resources/Car.png
new file mode 100644
index 0000000..95bcbcb
Binary files /dev/null and b/CityGame/Resources/Car.png differ
diff --git a/CityGame/Resources/Error.png b/CityGame/Resources/Error.png
new file mode 100644
index 0000000..1600d61
Binary files /dev/null and b/CityGame/Resources/Error.png differ
diff --git a/CityGame/Resources/ParkingLot.png b/CityGame/Resources/ParkingLot.png
new file mode 100644
index 0000000..932422f
Binary files /dev/null and b/CityGame/Resources/ParkingLot.png differ
diff --git a/CityGame/Resources/Road1.png b/CityGame/Resources/Road1.png
new file mode 100644
index 0000000..7fd0a2d
Binary files /dev/null and b/CityGame/Resources/Road1.png differ
diff --git a/CityGame/Resources/Road2.png b/CityGame/Resources/Road2.png
new file mode 100644
index 0000000..eee62e0
Binary files /dev/null and b/CityGame/Resources/Road2.png differ
diff --git a/CityGame/Resources/Road2c.png b/CityGame/Resources/Road2c.png
new file mode 100644
index 0000000..cc8426d
Binary files /dev/null and b/CityGame/Resources/Road2c.png differ
diff --git a/CityGame/Resources/Road3c.png b/CityGame/Resources/Road3c.png
new file mode 100644
index 0000000..6ec0c9c
Binary files /dev/null and b/CityGame/Resources/Road3c.png differ
diff --git a/CityGame/Resources/Road4c.png b/CityGame/Resources/Road4c.png
new file mode 100644
index 0000000..78f7434
Binary files /dev/null and b/CityGame/Resources/Road4c.png differ
diff --git a/CityGame/Resources/Tree.png b/CityGame/Resources/Tree.png
new file mode 100644
index 0000000..f909081
Binary files /dev/null and b/CityGame/Resources/Tree.png differ
diff --git a/CityGame/Resources/Vent1.png b/CityGame/Resources/Vent1.png
new file mode 100644
index 0000000..9f206a6
Binary files /dev/null and b/CityGame/Resources/Vent1.png differ
diff --git a/CityGame/Resources/Vent2.png b/CityGame/Resources/Vent2.png
new file mode 100644
index 0000000..d058111
Binary files /dev/null and b/CityGame/Resources/Vent2.png differ
diff --git a/CityGame/Resources/Vent3.png b/CityGame/Resources/Vent3.png
new file mode 100644
index 0000000..6d95883
Binary files /dev/null and b/CityGame/Resources/Vent3.png differ
diff --git a/CityGame/SourcedImage.cs b/CityGame/SourcedImage.cs
new file mode 100644
index 0000000..05d5831
--- /dev/null
+++ b/CityGame/SourcedImage.cs
@@ -0,0 +1,69 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+
+namespace CityGame
+{
+ public class SourcedImage : Image
+ {
+ public Dictionary> Alternatives = new Dictionary>()
+ {
+ };
+
+ public static Dictionary loadedSources = new Dictionary();
+
+ public SourcedImage(string source)
+ {
+ Source = SourceToImage(source);
+ Height = 64;
+ Width = 64;
+
+ ImageFailed += SourcedImage_ImageFailed;
+ }
+
+ private void SourcedImage_ImageFailed(object? sender, ExceptionRoutedEventArgs e)
+ {
+ Debug.WriteLine("failed");
+ }
+
+ public BitmapSource SourceToImage(string src, UriKind kind = UriKind.Absolute)
+ {
+ string degs = src.Split(':').Last();
+ string uri = src;
+ int deg = 0;
+ if (degs == "0" || degs == "90" || degs == "180" || degs == "270" || degs == "360" || degs == "450" || degs == "540" || degs == "630" || degs == "720")
+ {
+ deg = Convert.ToInt32(degs);
+ uri = string.Join(":", src.Split(':').Take(src.Split(':').Count() - 1).ToArray());
+ }
+ string last = "";
+ while (Alternatives.ContainsKey(uri) && uri != last)
+ {
+ last = uri;
+ List alts = Alternatives[uri];
+ alts.Add(uri);
+ uri = alts[MainWindow.random.Next(0, alts.Count)];
+ }
+ uri = Environment.CurrentDirectory + "\\Resources\\" + uri;
+ if (loadedSources.ContainsKey(src))
+ {
+ return loadedSources[src];
+ }
+ else
+ {
+ if(!File.Exists(uri))
+ {
+ uri = Environment.CurrentDirectory + "\\Resources\\ErrorRed.png";
+ }
+ loadedSources.Add(src, new TransformedBitmap(new BitmapImage(new Uri(uri, kind)), new RotateTransform(deg)));
+ return loadedSources[src];
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/CityGame/Tile.cs b/CityGame/Tile.cs
new file mode 100644
index 0000000..b95e8bc
--- /dev/null
+++ b/CityGame/Tile.cs
@@ -0,0 +1,10 @@
+namespace CityGame
+{
+ public struct Tile
+ {
+ public int BlockID;
+ public TileType Type;
+ public int X;
+ public int Y;
+ }
+}
\ No newline at end of file
diff --git a/CityGame/TileType.cs b/CityGame/TileType.cs
new file mode 100644
index 0000000..c829311
--- /dev/null
+++ b/CityGame/TileType.cs
@@ -0,0 +1,17 @@
+namespace CityGame
+{
+ public enum TileType
+ {
+ Skyscraper = 100,
+ House = 101,
+
+ Park = 200,
+
+ Lake = 300,
+ River = 301,
+
+ Road = 400,
+ Path = 401,
+ Bridge = 410
+ }
+}
\ No newline at end of file