CityGame/CityGame/MainWindow.xaml.cs
2023-05-07 22:50:39 +02:00

451 lines
No EOL
18 KiB
C#

using AStar;
using AStar.Options;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
using SimplexNoise;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using WPFGame;
namespace CityGame
{
public class Program { public static void Main() { new MainWindow(); } }
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public ISelectable GetSelectableFromClick(MouseState click)
{
Point point = new Point(click.X, click.Y);
point.X -= (int)Canvas.GetLeft(CameraCanvas);
point.Y -= (int)Canvas.GetTop(CameraCanvas);
point.X = (int)(point.X / CameraCanvas.ScaleX);
point.Y = (int)(point.Y / CameraCanvas.ScaleY);
foreach (Entity entity in Entities)
{
double diff = new Vector2((int)entity.X + TileSize / 2 - point.X, (int)entity.Y + TileSize / 2 - point.Y).Length();
if (diff < TileSize / 2) return entity;
}
int x = point.X / TileSize;
int y = point.Y / TileSize;
if (x < 0 || y < 0) return null;
if (x > Grid.GetLength(0) - 1 || y > Grid.GetLength(1) - 1) return null;
return Grid[x, y];
}
public static Entity GetEntityFromImage(Image image)
{
foreach (Entity entity in Entities)
{
if (entity.Object == image.Parent) return entity;
}
return null;
}
public static Tile? GetTileFromImage(Image image)
{
double x = Canvas.GetLeft(image.Parent) / TileSize;
double y = Canvas.GetTop(image.Parent) / TileSize;
if (x % 1 != 0 || y % 1 != 0) return null;
return Grid[(int)x, (int)y];
}
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 short[,] pathfindingGridDesperate;
public static List<Tile> npcWalkable = new List<Tile>();
public static List<Entity> Entities { get; set; } = new List<Entity>();
public const int TileSize = 64;
public static Tile[,] Grid;
public static ISelectable Selected;
public static OCanvas[,] ImageGrid;
Canvas MainCanvas = new OCanvas();
Canvas BGCanvas = new OCanvas();
Canvas GameCanvas = new OCanvas();
Canvas CameraCanvas = new OCanvas();
Canvas UICanvas = new OCanvas();
public MainWindow()
{
AddPenumbra();
#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("Road" + code, "Highway" + code, ColorConversionMaps.RoadToHighwayMap);
ImageConverter.ChangeColor("Road" + code, "HighwayBridge" + code, ColorConversionMaps.RoadToHighwayMap);
}
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<string, string> { { "#000000", "#ff0000" } });
#endregion
int seed = 8;
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 = (int)Math.Ceiling(mapHeight * mapWidth / 100f);
//NPCCount = 1;
random = new Random(seed);
#region | Map Initialization |
Tile[,] InitialGrid = new Tile[mapWidth, mapHeight];
List<Tuple<int, int>> coords = new List<Tuple<int, int>>();
for (int y = 0; y < mapHeight; y++)
{
for (int x = 0; x < mapWidth; x++)
{
coords.Add(new Tuple<int, int>(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;
}
}
Dictionary<int, bool> decidedBridges = new Dictionary<int, bool>();
pathfindingGrid = new short[doubleWidth, doubleHeight];
pathfindingGridDesperate = new short[doubleWidth, doubleHeight];
List<Tile> bridgeTiles = new List<Tile>();
List<Tile> roadTiles = new List<Tile>();
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;
}
}
int mainRoadCount = random.Next(1, 5);
for (int m = 0; m < mainRoadCount; m++)
{
int variant = random.Next(0, 2);
IntPoint startPoint = new IntPoint(0, 0);
IntPoint endPoint = new IntPoint(0, 0);
IntPoint step = new IntPoint(0, 0);
if (variant == 0)
{
int x = 0;
int y = random.Next(0 + 2, doubleHeight - 2);
startPoint = new IntPoint(x, y);
endPoint = new IntPoint(doubleWidth, y);
step = new IntPoint(1, 0);
}
else
{
int x = random.Next(0 + 2, doubleWidth - 2);
int y = 0;
startPoint = new IntPoint(x, y);
endPoint = new IntPoint(x, doubleHeight);
step = new IntPoint(0, 1);
}
while (startPoint != endPoint)
{
int x = (int)startPoint.X;
int y = (int)startPoint.Y;
var tile = IntermediateGrid[x, y];
IntermediateGrid[x, y].Type = tile.Type == TileType.Lake || tile.Type == TileType.River ? TileType.HighwayBridge : TileType.Highway;
var topRoad = IntermediateGrid[x - step.Y, y - step.X];
var checkTopRoad = IntermediateGrid[x - step.Y * 2, y - step.X * 2];
var bottomRoad = IntermediateGrid[x + step.Y, y + step.X];
var checkBottomRoad = IntermediateGrid[x + step.Y * 2, y + step.X * 2];
if (((int)topRoad.Type) / 100 == 4 && ((int)checkTopRoad.Type) / 100 != 4) IntermediateGrid[x - step.Y, y - step.X].Type = topRoad.Type == TileType.Road ? TileType.Skyscraper : topRoad.Type == TileType.Path ? TileType.Park : TileType.Lake;
if (((int)bottomRoad.Type) / 100 == 4 && ((int)checkBottomRoad.Type) / 100 != 4) IntermediateGrid[x + step.Y, y + step.X].Type = TileType.Skyscraper;
if (((int)topRoad.Type) % 100 / 10 == 1 && ((int)bottomRoad.Type) % 100 / 10 == 1) IntermediateGrid[x, y].Type = TileType.HighwayBridge;
startPoint += step;
}
}
for (int y = 0; y < doubleHeight; y++)
{
for (int x = 0; x < doubleWidth; x++)
{
var type = IntermediateGrid[x, y].Type;
bool walkable = ((int)type) / 100 == 4;
pathfindingGridDesperate[y, x] = (short)(walkable ? 1 : 0);
if (type == TileType.Path) walkable = false;
pathfindingGrid[y, x] = (short)(walkable ? 1 : 0);
if (type == TileType.Bridge) bridgeTiles.Add(IntermediateGrid[x, y]);
if (type == TileType.Road) roadTiles.Add(IntermediateGrid[x, y]);
if (type == TileType.Road || type == TileType.Bridge) npcWalkable.Add(IntermediateGrid[x, y]);
}
}
var worldGrid = new WorldGrid(pathfindingGrid);
pathfinder = new PathFinder(worldGrid, new PathFinderOptions { PunishChangeDirection = true, UseDiagonals = false, SearchLimit = int.MaxValue, HeuristicFormula = AStar.Heuristics.HeuristicFormula.Euclidean });
#endregion
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;
ImageGrid = new OCanvas[doubleWidth, doubleHeight];
Canvas.SetLeft(CameraCanvas, 0);
Canvas.SetTop(CameraCanvas, 0);
MainCanvas.Children.Add(CameraCanvas);
MainCanvas.Children.Add(UICanvas);
CameraCanvas.Children.Add(BGCanvas);
CameraCanvas.Children.Add(GameCanvas);
ContentCanvas = 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);
Canvas.SetLeft(image, x * tileSize);
Canvas.SetTop(image, y * tileSize);
ImageGrid[x, y] = (OCanvas)image;
//image.Opacity = pathfindingGrid[y, x];
GameCanvas.Children.Add(image);
}
}
foreach (Image image in SourcedImage.GetObjectsBySourceFile("Helipad.png"))
{
float x = (float)Canvas.GetLeft(image.Parent);
float y = (float)Canvas.GetTop(image.Parent);
Entities.Add(new Helicopter { X = x, Y = y });
}
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)];
car.Target = new Point(targetTile.X, targetTile.Y);
};
car.JourneyFinished += reset;
car.JourneyImpossible += reset;
Entities.Add(car);
}
Show();
}
int swv;
protected override void Update(GameTime time)
{
MouseState state = Mouse.GetState();
if (state.MiddleButton == ButtonState.Pressed)
{
var newpos = new Point(state.X, state.Y);
var diff = newpos - MousePos;
Canvas.SetLeft(CameraCanvas, Canvas.GetLeft(CameraCanvas) + diff.X);
Canvas.SetTop(CameraCanvas, Canvas.GetTop(CameraCanvas) + diff.Y);
}
MousePos = new Point(state.X, state.Y);
float delta = state.ScrollWheelValue - swv;
swv = state.ScrollWheelValue;
if (delta != 0)
{
Debug.WriteLine(delta);
}
float multi = 1.05f;
if (delta < 0) multi = 1 / multi;
if (delta != 0)
{
CameraCanvas.ScaleX *= multi;
CameraCanvas.ScaleY *= multi;
}
if (state.LeftButton == ButtonState.Pressed)
{
ISelectable select = GetSelectableFromClick(state);
if (select is not null)
{
if (Selected is not null) Selected.GetImage().Opacity = 1;
Selected = select;
Selected.GetImage().Opacity = 0.5f;
}
}
else if (state.RightButton == ButtonState.Pressed)
{
ISelectable select = GetSelectableFromClick(state);
if (select is not null)
{
Selected.RunAction(select);
}
}
long milliseconds = DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond;
foreach (Entity entity in Entities)
{
long deltaTime = milliseconds - entity.Time;
entity.Time = milliseconds;
entity.Tick(deltaTime);
if (entity.Object is null)
{
entity.Object = entity.Render();
GameCanvas.Children.Add(entity.Object);
}
entity.Object.Rotation = (int)entity.Rotation;
Canvas.SetLeft(entity.Object, entity.X);
Canvas.SetTop(entity.Object, entity.Y);
}
}
}
}