diff --git a/.gitignore b/.gitignore
index fea8d3f..fa6f4c7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,4 @@
.config/
.vscode/
-src/bin
-src/**/obj
\ No newline at end of file
+**/bin/
+**/obj/
\ No newline at end of file
diff --git a/Content/Content.mgcb b/Content/Content.mgcb
new file mode 100644
index 0000000..15f5d4e
--- /dev/null
+++ b/Content/Content.mgcb
@@ -0,0 +1,30 @@
+
+#----------------------------- Global Properties ----------------------------#
+
+/outputDir:bin/$(Platform)
+/intermediateDir:obj/$(Platform)
+/platform:DesktopGL
+/config:
+/profile:Reach
+/compress:False
+
+#-------------------------------- References --------------------------------#
+
+
+#---------------------------------- Content ---------------------------------#
+
+#begin images/atlas-definition.xml
+/copy:images/atlas-definition.xml
+
+#begin images/atlas.png
+/importer:TextureImporter
+/processor:TextureProcessor
+/processorParam:ColorKeyColor=255,0,255,255
+/processorParam:ColorKeyEnabled=True
+/processorParam:GenerateMipmaps=False
+/processorParam:PremultiplyAlpha=True
+/processorParam:ResizeToPowerOfTwo=False
+/processorParam:MakeSquare=False
+/processorParam:TextureFormat=Color
+/build:images/atlas.png
+
diff --git a/Content/images/atlas-definition.xml b/Content/images/atlas-definition.xml
new file mode 100755
index 0000000..4002841
--- /dev/null
+++ b/Content/images/atlas-definition.xml
@@ -0,0 +1,7 @@
+
+
+ images/atlas
+
+
+
+
diff --git a/Content/images/atlas.kra b/Content/images/atlas.kra
new file mode 100644
index 0000000..32e204d
Binary files /dev/null and b/Content/images/atlas.kra differ
diff --git a/Content/images/atlas.png b/Content/images/atlas.png
new file mode 100644
index 0000000..02346da
Binary files /dev/null and b/Content/images/atlas.png differ
diff --git a/Game1.cs b/Game1.cs
new file mode 100644
index 0000000..2715c9e
--- /dev/null
+++ b/Game1.cs
@@ -0,0 +1,140 @@
+using System;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+using Microsoft.Xna.Framework.Input;
+
+using MonoGameLibrary;
+using MonoGameLibrary.Graphics;
+
+// using tunnet.World;
+
+namespace tunnet;
+
+public class Game1 : Core
+{
+ // private World.Map _world;
+ // private Texture2D _logo;
+
+ public Game1() : base("Tunnet but not", 1280, 720, fullScreen: false)
+ {
+ // Content.RootDirectory = "Content";
+ // IsMouseVisible = true;
+ }
+
+ protected override void Initialize()
+ {
+ // TODO: Add your initialization logic here
+ // _world = new World.Map(mapId: 0);
+ // _world.LoadMap();
+ //
+
+ base.Initialize();
+ }
+
+ private Sprite _404;
+ // private AnimatedSprite _animatedSprite;
+ protected override void LoadContent()
+ {
+ TextureAtlas atlas = TextureAtlas.FromFile(Content, "images/atlas-definition.xml");
+
+ _404 = atlas.CreateSprite("404");
+ // _animatedSprite = atlas.CreateAnimatedSprite("404");
+ }
+
+ private const int _CAMERA_SPEED = 5;
+ Viewport _cameraPosition;
+ Point _mousePosition;
+ protected override void Update(GameTime gameTime)
+ {
+ if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || Keyboard.GetState().IsKeyDown(Keys.Escape))
+ Exit();
+
+ // TODO: Add your update logic here
+
+ // Animations
+ // _animatedSprite.Update(gameTime);
+
+ // Inputs
+ // https://docs.monogame.net/articles/tutorials/building_2d_games/10_handling_input/
+
+ if (_cameraPosition.Equals(new Viewport())) _cameraPosition = GraphicsDevice.Viewport;
+
+ CheckKeyboardInput();
+ CheckMouseInput();
+
+ base.Update(gameTime);
+ }
+ private void CheckKeyboardInput()
+ {
+ // Get the state of keyboard input
+ KeyboardState keyboardState = Keyboard.GetState();
+
+ // If the space key is held down, the movement speed increases by 1.5
+ int speed = _CAMERA_SPEED;
+ if (keyboardState.IsKeyDown(Keys.Space))
+ {
+ speed = (int)Math.Floor(speed * 1.5);
+ }
+ if (keyboardState.IsKeyDown(Keys.W) || keyboardState.IsKeyDown(Keys.Up))
+ {
+ _cameraPosition.Y += speed;
+ }
+ if (keyboardState.IsKeyDown(Keys.S) || keyboardState.IsKeyDown(Keys.Down))
+ {
+ _cameraPosition.Y -= speed;
+ }
+ if (keyboardState.IsKeyDown(Keys.A) || keyboardState.IsKeyDown(Keys.Left))
+ {
+ _cameraPosition.X += speed;
+ }
+ if (keyboardState.IsKeyDown(Keys.D) || keyboardState.IsKeyDown(Keys.Right))
+ {
+ _cameraPosition.X -= speed;
+ }
+ //
+ GraphicsDevice.Viewport = _cameraPosition;
+ }
+ private void CheckMouseInput()
+ {
+ // Get the state of mouse input
+ MouseState mouseState = Mouse.GetState();
+
+ //Move camera with right mouse button
+ if (mouseState.RightButton == ButtonState.Pressed)
+ {
+ _mousePosition -= mouseState.Position; // delta
+
+ _cameraPosition!.X = _cameraPosition.X - _mousePosition.X;
+ _cameraPosition.Y = _cameraPosition.Y - _mousePosition.Y;
+ }
+ //
+ GraphicsDevice.Viewport = _cameraPosition;
+ _mousePosition = mouseState.Position; // here because of wasd breaking it
+ }
+
+ protected override void Draw(GameTime gameTime)
+ {
+ GraphicsDevice.Clear(Color.CornflowerBlue);
+
+ SpriteBatch.Begin(sortMode: SpriteSortMode.Deferred);
+
+ // World.Map.Draw draw = new World.Map.Draw(in _spriteBatch); //TODO: fix
+ // _spriteBatch.Draw(_world._defaultTile, _world._defaultTile.Bounds.Center.ToVector2(), Color.White); //TODO: fix
+
+ /// TODO: Draw
+ /// 1. floor
+ /// 2. wires
+ /// 3. buildings
+ /// 4. entities
+ /// 5. walls
+ ///
+ /// MINIMIZE texture swaps
+ /// MAXIMIZE drawRectangle usage per texture
+
+ _404.Draw(SpriteBatch, Vector2.Zero);
+
+ SpriteBatch.End();
+ //
+ base.Draw(gameTime);
+ }
+}
diff --git a/src/Icon.bmp b/Icon.bmp
similarity index 100%
rename from src/Icon.bmp
rename to Icon.bmp
diff --git a/src/Icon.ico b/Icon.ico
similarity index 100%
rename from src/Icon.ico
rename to Icon.ico
diff --git a/src/Program.cs b/Program.cs
similarity index 100%
rename from src/Program.cs
rename to Program.cs
diff --git a/World/World.cs b/World/World.cs
new file mode 100644
index 0000000..693b544
--- /dev/null
+++ b/World/World.cs
@@ -0,0 +1,210 @@
+// 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
+ {
+ ///
+ /// Saves the given chunk to disk in a compact binary format:
+ /// Width×Height consecutive Int32 tile IDs.
+ ///
+ 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);
+ }
+
+ ///
+ /// Loads a chunk from the binary file format produced by Save().
+ ///
+ 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 _loadedChunks = new Dictionary();
+ private IReadOnlyDictionary 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();
+ 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 _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);
+ }
+ }
+ }
+ }*/
+ }
+}
diff --git a/src/app.manifest b/app.manifest
similarity index 100%
rename from src/app.manifest
rename to app.manifest
diff --git a/run.sh b/run.sh
index ef64ddd..47c536a 100755
--- a/run.sh
+++ b/run.sh
@@ -1,4 +1,4 @@
#!/usr/bin/env bash
echo "Running..."
-exec dotnet run --no-build --project src/
+exec dotnet run --no-build
diff --git a/src/Content/Content.mgcb b/src/Content/Content.mgcb
deleted file mode 100644
index ddc4c36..0000000
--- a/src/Content/Content.mgcb
+++ /dev/null
@@ -1,15 +0,0 @@
-
-#----------------------------- Global Properties ----------------------------#
-
-/outputDir:bin/$(Platform)
-/intermediateDir:obj/$(Platform)
-/platform:DesktopGL
-/config:
-/profile:Reach
-/compress:False
-
-#-------------------------------- References --------------------------------#
-
-
-#---------------------------------- Content ---------------------------------#
-
diff --git a/src/Game1.cs b/src/Game1.cs
deleted file mode 100644
index 061b2b7..0000000
--- a/src/Game1.cs
+++ /dev/null
@@ -1,51 +0,0 @@
-using Microsoft.Xna.Framework;
-using Microsoft.Xna.Framework.Graphics;
-using Microsoft.Xna.Framework.Input;
-
-namespace tunnet;
-
-public class Game1 : Game
-{
- private GraphicsDeviceManager _graphics;
- private SpriteBatch _spriteBatch;
-
- public Game1()
- {
- _graphics = new GraphicsDeviceManager(this);
- Content.RootDirectory = "Content";
- IsMouseVisible = true;
- }
-
- protected override void Initialize()
- {
- // TODO: Add your initialization logic here
-
- base.Initialize();
- }
-
- protected override void LoadContent()
- {
- _spriteBatch = new SpriteBatch(GraphicsDevice);
-
- // TODO: use this.Content to load your game content here
- }
-
- protected override void Update(GameTime gameTime)
- {
- if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || Keyboard.GetState().IsKeyDown(Keys.Escape))
- Exit();
-
- // TODO: Add your update logic here
-
- base.Update(gameTime);
- }
-
- protected override void Draw(GameTime gameTime)
- {
- GraphicsDevice.Clear(Color.CornflowerBlue);
-
- // TODO: Add your drawing code here
-
- base.Draw(gameTime);
- }
-}
diff --git a/tunnet.code-workspace b/tunnet.code-workspace
new file mode 100644
index 0000000..de141ec
--- /dev/null
+++ b/tunnet.code-workspace
@@ -0,0 +1,11 @@
+{
+ "folders": [
+ {
+ "path": "."
+ },
+ {
+ "path": "../lib-bird"
+ }
+ ],
+ "settings": {}
+}
\ No newline at end of file
diff --git a/src/tunnet.csproj b/tunnet.csproj
similarity index 90%
rename from src/tunnet.csproj
rename to tunnet.csproj
index 06680d2..3a77701 100644
--- a/src/tunnet.csproj
+++ b/tunnet.csproj
@@ -5,6 +5,7 @@
Major
false
false
+
app.manifest
@@ -26,6 +27,9 @@
+
+
+
diff --git a/tunnet.sln b/tunnet.sln
index aa36c25..e3c59bc 100644
--- a/tunnet.sln
+++ b/tunnet.sln
@@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.0.31903.59
MinimumVisualStudioVersion = 10.0.40219.1
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "tunnet", "src/tunnet.csproj", "{728D0B55-232F-4C2B-A31F-04EA4BFF4209}"
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "tunnet", "tunnet.csproj", "{728D0B55-232F-4C2B-A31F-04EA4BFF4209}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution