210 lines
6.5 KiB
C#
210 lines
6.5 KiB
C#
// using System;
|
||
using System.Collections.Generic;
|
||
using System.IO;
|
||
using System.Threading;
|
||
using Microsoft.Xna.Framework;
|
||
// using Microsoft.Xna.Framework.Content;
|
||
using Microsoft.Xna.Framework.Graphics;
|
||
|
||
namespace tunnet.World;
|
||
|
||
struct World
|
||
{
|
||
private class Tile // TODO: sprite, buildings[]
|
||
{
|
||
public int Id { get; }
|
||
public Point point { get; }
|
||
|
||
public Tile(int id) => Id = id;
|
||
public static Tile FromId(int id)
|
||
{
|
||
return new Tile(id);
|
||
}
|
||
}
|
||
private class Chunk
|
||
{
|
||
public const int Width = 32;
|
||
public const int Height = 32;
|
||
internal Tile[,] Tiles { get; private set; }
|
||
|
||
public Chunk()
|
||
{
|
||
Tiles = new Tile[Width, Height];
|
||
}
|
||
}
|
||
private static class ChunkStorage
|
||
{
|
||
/// <summary>
|
||
/// Saves the given chunk to disk in a compact binary format:
|
||
/// Width×Height consecutive Int32 tile IDs.
|
||
/// </summary>
|
||
public static void Save(string filePath, Chunk chunk)
|
||
{
|
||
if (chunk == null)
|
||
throw new System.ArgumentNullException(nameof(chunk));
|
||
|
||
Directory.CreateDirectory(Path.GetDirectoryName(filePath) ?? ".");
|
||
using var fs = new FileStream(filePath, FileMode.Create, FileAccess.Write);
|
||
using var writer = new BinaryWriter(fs);
|
||
|
||
for (int x = 0; x < Chunk.Width; x++)
|
||
{
|
||
for (int y = 0; y < Chunk.Height; y++)
|
||
{
|
||
// write each tile’s ID
|
||
writer.Write(chunk.Tiles[x, y]?.Id ?? 0);
|
||
}
|
||
}
|
||
}
|
||
public static void Save(int chunkX, int chunkY, Chunk chunk)
|
||
{
|
||
Save($"{chunkX}_{chunkY}.chunk", chunk);
|
||
}
|
||
|
||
/// <summary>
|
||
/// Loads a chunk from the binary file format produced by Save().
|
||
/// </summary>
|
||
public static Chunk Load(string filePath)
|
||
{
|
||
if (!File.Exists(filePath))
|
||
throw new FileNotFoundException("Chunk file not found", filePath);
|
||
//TODO generate chunk instead
|
||
|
||
var chunk = new Chunk();
|
||
using var fs = new FileStream(filePath, FileMode.Open, FileAccess.Read);
|
||
using var reader = new BinaryReader(fs);
|
||
|
||
for (int x = 0; x < Chunk.Width; x++)
|
||
for (int y = 0; y < Chunk.Height; y++)
|
||
{
|
||
int id = reader.ReadInt32();
|
||
chunk.Tiles[x, y] = Tile.FromId(id);
|
||
}
|
||
|
||
return chunk;
|
||
}
|
||
public static Chunk Load(Point location)
|
||
{
|
||
var filePath = $"{location.X}_{location.Y}.chunk";
|
||
if (File.Exists(filePath))
|
||
return Load(filePath);
|
||
return null;
|
||
}
|
||
}
|
||
public class Map
|
||
{
|
||
private readonly Dictionary<Point, Chunk> _loadedChunks = new Dictionary<Point, Chunk>();
|
||
private IReadOnlyDictionary<Point, Chunk> Chunks => _loadedChunks;
|
||
private Mutex _loadWait = new();
|
||
private readonly int id;
|
||
// private readonly int TileSize;
|
||
// public Texture2D _defaultTile;
|
||
|
||
public Map(int mapId)
|
||
{
|
||
id = mapId;
|
||
// TileSize = 32;
|
||
}
|
||
|
||
// Load the map by ID
|
||
public void LoadMap()
|
||
{
|
||
//TODO: Load map by ID
|
||
}
|
||
|
||
// Generate a new map
|
||
private void GenerateMap()
|
||
{
|
||
//TODO: Implement map generation
|
||
}
|
||
|
||
// Get a chunk (load if not in memory)
|
||
private Chunk GetChunk(Point location)
|
||
{
|
||
// var key = new Point(chunkX, chunkY);
|
||
if (!_loadedChunks.TryGetValue(location, out Chunk chunk))
|
||
{
|
||
chunk = ChunkStorage.Load(location);// LoadChunkFromDisk(chunkX, chunkY); // Or generate
|
||
_loadedChunks[location] = chunk;
|
||
}
|
||
return chunk;
|
||
}
|
||
|
||
// Access a specific tile
|
||
private Tile GetTile(int worldX, int worldY)
|
||
{
|
||
int chunkX = worldX / Chunk.Width;
|
||
int chunkY = worldY / Chunk.Height;
|
||
int tileX = worldX % Chunk.Width;
|
||
int tileY = worldY % Chunk.Height;
|
||
|
||
// Handle negative coordinates
|
||
if (tileX < 0) tileX += Chunk.Width;
|
||
if (tileY < 0) tileY += Chunk.Height;
|
||
|
||
return GetChunk(new Point(chunkX, chunkY)).Tiles[tileX, tileY];
|
||
}
|
||
|
||
// Unload distant chunks (memory management)
|
||
public void UnloadChunks(Point currentChunk, int keepRadius)
|
||
{
|
||
var keysToRemove = new List<Point>();
|
||
foreach (var key in _loadedChunks.Keys)
|
||
{
|
||
if (System.Math.Abs(key.X - currentChunk.X) > keepRadius ||
|
||
System.Math.Abs(key.Y - currentChunk.Y) > keepRadius)
|
||
{
|
||
ChunkStorage.Save(key.X, key.Y, _loadedChunks[key]); // Persist if needed
|
||
keysToRemove.Add(key);
|
||
}
|
||
}
|
||
keysToRemove.ForEach(k => _loadedChunks.Remove(k));
|
||
}
|
||
|
||
/*public class Draw(in SpriteBatch spriteBatch)
|
||
{
|
||
private SpriteBatch spriteBatch = spriteBatch;
|
||
}*/
|
||
|
||
// private ref struct Draw
|
||
// {
|
||
// private ref SpriteBatch _spriteBatch;
|
||
// public Draw(ref SpriteBatch spriteBatch)
|
||
// {
|
||
// _spriteBatch = ref spriteBatch;
|
||
// }
|
||
// }
|
||
|
||
// private readonly IReadOnlyDictionary<Point, Chunk> _chunks;
|
||
|
||
/*public void Draw()
|
||
{
|
||
if (_chunks.Count == 0)
|
||
{
|
||
Console.WriteLine("No chunks loaded");
|
||
return;
|
||
}
|
||
foreach (var kvp in _chunks)
|
||
{
|
||
Point chunkCoord = kvp.Key;
|
||
// var chunk = kvp.Value;
|
||
|
||
for (int cx = 0; cx < Chunk.Width; cx++)
|
||
{
|
||
for (int cy = 0; cy < Chunk.Height; cy++)
|
||
{
|
||
// world‐space position in pixels
|
||
Vector2 pos = new Vector2(
|
||
(chunkCoord.X * Chunk.Width + cx) * TileSize,
|
||
(chunkCoord.Y * Chunk.Height + cy) * TileSize
|
||
);
|
||
|
||
// draw the default tile for now
|
||
Console.WriteLine("Sprite location: ", pos);
|
||
_spriteBatch.Draw(_defaultTile, pos, Color.White);
|
||
}
|
||
}
|
||
}
|
||
}*/
|
||
}
|
||
}
|