diff --git a/assets/level1.json b/assets/level1.json
index 85e9d91..66ad477 100644
--- a/assets/level1.json
+++ b/assets/level1.json
@@ -1,18 +1 @@
-[
- [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
- [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
- [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
- [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
- [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
- [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
- [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
- [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
- [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
- [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
- [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
- [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
- [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
- [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
- [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
- [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
-]
+[[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0],[1,2,2,2,2,2,2,0,2,2,3,2,4,0,2,2,2,2,2,4,2,4,2,2,2,2,2,2,2,0,0],[1,2,4,9,4,2,2,2,2,2,2,0,2,2,2,2,2,2,2,2,2,4,3,2,2,4,2,2,2,2,0],[1,2,2,2,2,2,2,2,2,2,2,0,2,2,0,2,2,2,2,2,4,2,4,2,2,4,2,2,2,2,0],[1,4,2,0,0,2,2,2,2,2,2,2,2,2,4,2,2,2,2,2,2,4,2,2,4,2,2,2,2,4,0],[1,4,2,4,4,2,2,2,2,2,2,2,2,2,4,4,2,2,4,2,2,2,2,2,2,2,2,4,2,2,0],[1,2,2,2,4,2,2,4,2,2,2,2,2,2,2,2,4,2,2,2,2,2,4,2,0,4,2,2,2,2,0],[1,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,0],[1,2,0,2,2,2,4,2,2,3,2,0,2,2,4,2,4,2,2,2,2,2,2,2,2,2,2,3,2,4,2],[1,2,2,3,2,2,2,2,2,4,2,2,2,2,2,0,2,2,2,2,2,2,2,2,4,0,0,4,2,2,0],[1,2,2,2,4,2,2,4,2,4,2,2,2,2,2,2,2,2,2,2,2,2,2,2,4,4,2,4,2,2,0],[1,2,0,2,2,2,2,2,4,2,2,2,2,2,2,2,2,4,4,2,2,2,2,2,2,2,2,4,2,2,0],[1,2,4,2,2,2,2,2,4,2,2,0,0,2,2,2,2,4,2,4,3,2,2,3,2,2,2,2,4,2,0],[1,2,3,4,2,2,2,2,2,2,2,2,2,2,2,2,2,2,4,4,4,2,2,4,2,2,2,2,2,2,0],[1,2,2,2,2,2,2,2,2,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,0],[1,0,0,2,2,2,2,2,2,2,2,2,0,2,2,2,3,2,2,2,2,4,2,2,2,2,2,4,2,2,0]]
\ No newline at end of file
diff --git a/boulder-dash.csproj b/boulder-dash.csproj
index 45b0e45..4f42387 100644
--- a/boulder-dash.csproj
+++ b/boulder-dash.csproj
@@ -12,7 +12,6 @@
-
diff --git a/src/BoulderDash.cs b/src/BoulderDash.cs
index 6ee57f4..2dab464 100644
--- a/src/BoulderDash.cs
+++ b/src/BoulderDash.cs
@@ -18,11 +18,13 @@ class BoulderDash : Game
private void registerScenes()
{
GameState.sceneManager.AddScene("main", new MainMenu());
+ GameState.sceneManager.AddScene("level", new Level());
}
protected override void update(double dt)
{
SDL_Event e;
+ GameState.saving = false;
while (SDL_PollEvent(out e) != 0)
{
if (e.type == SDL_EventType.SDL_QUIT)
@@ -31,14 +33,20 @@ class BoulderDash : Game
}
if (e.type == SDL_EventType.SDL_KEYDOWN)
{
+ GameState.sceneManager.GetCurrentScene()?.OnKeyDown(context, e.key.keysym.sym, e.key.keysym.mod);
if (e.key.keysym.sym == SDL_Keycode.SDLK_ESCAPE)
{
System.Environment.Exit(0);
}
}
+ if (e.type == SDL_EventType.SDL_MOUSEBUTTONDOWN)
+ {
+ GameState.sceneManager.GetCurrentScene()?.OnMouseDown(context, e.button.button, e.button.x, e.button.y);
+ }
}
GameState.keyState = new KeyState();
- GameState.sceneManager.GetCurrentScene()?.Update(dt, context);
+ GameState.tick = (GameState.tick + 1) % Game.TPS;
+ GameState.sceneManager.GetCurrentScene()?.Update(context);
}
protected override void draw()
@@ -48,12 +56,6 @@ class BoulderDash : Game
#endif
this.context.renderer.Clear();
GameState.sceneManager.GetCurrentScene()?.Draw(this.context);
- this.context.renderer.DrawTileSet(this.tileSet, 0, 0, 1, 6);
- this.context.renderer.DrawTileSet(this.tileSet, 32, 0, 1, 6);
- this.context.renderer.DrawTileSet(this.tileSet, 64, 0, 1, 6);
- this.context.renderer.DrawTileSet(this.tileSet, 96, 0, 1, 6);
- this.context.renderer.DrawTileSet(this.tileSet, 128, 0, 1, 6);
- this.context.renderer.DrawTileSet(this.tileSet, 160, 0, 1, 6);
this.context.renderer.Present();
SDL_Delay(1);
}
diff --git a/src/Controls.cs b/src/Controls.cs
index 8bf1812..9ffc27a 100644
--- a/src/Controls.cs
+++ b/src/Controls.cs
@@ -7,6 +7,7 @@ enum Control
LEFT,
RIGHT,
STAY,
+ CONFIRM,
}
static class ControlKeyExtension
@@ -25,6 +26,8 @@ static class ControlKeyExtension
return SDL_Keycode.SDLK_d;
case Control.STAY:
return SDL_Keycode.SDLK_LCTRL;
+ case Control.CONFIRM:
+ return SDL_Keycode.SDLK_SPACE;
default:
throw new ArgumentException("Invalid control");
}
diff --git a/src/actors/Map.cs b/src/actors/Map.cs
index 0f3b176..037e0b0 100644
--- a/src/actors/Map.cs
+++ b/src/actors/Map.cs
@@ -1,25 +1,138 @@
-using Newtonsoft.Json;
+// using Newtonsoft.Json;
+using System.Text.Json;
class Map : Actor
{
private int[][] map;
+ private Dictionary tiles;
- public Map(uint width, uint height)
+ public Map(uint width, uint height, Dictionary tiles)
{
this.map = new int[height][];
for (uint i = 0; i < height; i++)
{
this.map[i] = new int[width];
}
+ this.tiles = tiles;
}
- public Map(string json)
+ public Map(string json, Dictionary tiles)
{
- var map = JsonConvert.DeserializeObject(json);
+ var map = JsonSerializer.Deserialize(json);
+
if (map == null)
{
throw new Exception("Invalid map");
}
this.map = map;
+ this.tiles = tiles;
+ }
+
+ public string ToJson()
+ {
+ return JsonSerializer.Serialize(this.map);
+ }
+
+ public void Dump()
+ {
+ for (uint i = 0; i < this.map.Length; i++)
+ {
+ var row = String.Join("", this.map[i]);
+ Console.WriteLine(row);
+ }
+ }
+
+ public override void Draw(Context context)
+ {
+ for (int y = 0; y < this.map.Length; y++)
+ {
+ for (int x = 0; x < this.map[y].Length; x++)
+ {
+ if (this.tiles.ContainsKey(this.map[y][x]))
+ {
+ var tile = this.tiles[this.map[y][x]];
+ tile.Draw(context, x * 32, y * 32);
+ }
+ }
+ }
+ }
+
+ public override void Update(Context context)
+ {
+ for (int y = 0; y < this.map.Length; y++)
+ {
+ for (int x = 0; x < this.map[y].Length; x++)
+ {
+ if (this.tiles.ContainsKey(this.map[y][x]))
+ {
+ var tile = this.tiles[this.map[y][x]];
+ tile.Update(context, this, x, y);
+ }
+ }
+ }
+ }
+
+ public bool IsSolid(int x, int y)
+ {
+ if (this.tiles.ContainsKey(this.map[y][x]))
+ {
+ var tile = this.tiles[this.map[y][x]];
+ return tile.IsSolid();
+ }
+ return false;
+ }
+
+ public bool IsSemiSolid(int x, int y)
+ {
+ if (this.tiles.ContainsKey(this.map[y][x]))
+ {
+ var tile = this.tiles[this.map[y][x]];
+ return tile.IsSemiSolid();
+ }
+ return false;
+ }
+
+ public void SetTile(int x, int y, int tile)
+ {
+ if (this.map.Length <= y)
+ {
+ // expand map
+ var newMap = new int[y + 1][];
+ for (int i = 0; i < y + 1; i++)
+ {
+ newMap[i] = new int[this.map[0].Length];
+ }
+ for (int i = 0; i < this.map.Length; i++)
+ {
+ for (int j = 0; j < this.map[i].Length; j++)
+ {
+ newMap[i][j] = this.map[i][j];
+ }
+ }
+ this.map = newMap;
+ }
+ if (this.map[y].Length <= x)
+ {
+ // expand map
+ var newMap = new int[this.map.Length][];
+ for (int i = 0; i < this.map.Length; i++)
+ {
+ newMap[i] = new int[x + 1];
+ }
+ for (int i = 0; i < this.map.Length; i++)
+ {
+ for (int j = 0; j < this.map[i].Length; j++)
+ {
+ newMap[i][j] = this.map[i][j];
+ }
+ }
+ this.map = newMap;
+ }
+ this.map[y][x] = tile;
+ }
+
+ public int GetTile(int x, int y)
+ {
+ return this.map[y][x];
}
}
diff --git a/src/actors/Tile.cs b/src/actors/Tile.cs
new file mode 100644
index 0000000..659dc74
--- /dev/null
+++ b/src/actors/Tile.cs
@@ -0,0 +1,31 @@
+class Tile
+{
+ private TileSet tileSet;
+ private int srcX, srcY;
+
+ public Tile(TileSet tileSet, int srcX, int srcY)
+ {
+ this.tileSet = tileSet;
+ this.srcX = srcX;
+ this.srcY = srcY;
+ }
+
+ public virtual void Draw(Context context, int x, int y)
+ {
+ context.renderer.DrawTileSet(tileSet, x, y, srcX, srcY);
+ }
+
+ public virtual void Update(Context context, Map map, int x, int y)
+ {
+ }
+
+ public virtual bool IsSolid()
+ {
+ return true;
+ }
+
+ public virtual bool IsSemiSolid()
+ {
+ return false;
+ }
+}
diff --git a/src/actors/Tiles/Diamond.cs b/src/actors/Tiles/Diamond.cs
new file mode 100644
index 0000000..e593ee8
--- /dev/null
+++ b/src/actors/Tiles/Diamond.cs
@@ -0,0 +1,28 @@
+class Diamond : Rock
+{
+ private TileSet tileSet;
+ private double i;
+ private int tileX;
+
+ public Diamond(TileSet tileSet, int srcX, int srcY) : base(tileSet, srcX, srcY)
+ {
+ this.tileSet = tileSet;
+ }
+
+ public override void Update(Context context, Map map, int x, int y)
+ {
+ ++i;
+ i = i % 128;
+ tileX = (int)(i * (8.0 / 128.0));
+ }
+
+ public override void Draw(Context context, int x, int y)
+ {
+ context.renderer.DrawTileSet(tileSet, x, y, tileX, 10);
+ }
+
+ public override bool IsSemiSolid()
+ {
+ return true;
+ }
+}
diff --git a/src/actors/Tiles/Dirt.cs b/src/actors/Tiles/Dirt.cs
new file mode 100644
index 0000000..04b2c5e
--- /dev/null
+++ b/src/actors/Tiles/Dirt.cs
@@ -0,0 +1,11 @@
+class Dirt : Tile
+{
+ public Dirt(TileSet tileSet, int srcX, int srcY) : base(tileSet, srcX, srcY)
+ {
+ }
+
+ public override bool IsSemiSolid()
+ {
+ return true;
+ }
+}
diff --git a/src/actors/Tiles/Player.cs b/src/actors/Tiles/Player.cs
new file mode 100644
index 0000000..9ea8976
--- /dev/null
+++ b/src/actors/Tiles/Player.cs
@@ -0,0 +1,56 @@
+class Player : Tile
+{
+ private int walkCD = 0;
+ const int TICKS_PAR_TILE = 10;
+
+ public Player(TileSet tileSet, int srcX, int srcY) : base(tileSet, srcX, srcY)
+ {
+ }
+
+ public override void Draw(Context context, int x, int y)
+ {
+ base.Draw(context, x, y);
+ }
+
+ public override void Update(Context context, Map map, int x, int y)
+ {
+ if (GameState.keyState.isPressed(Control.UP) && walkCD is 0)
+ {
+ if (!map.IsSolid(x, y - 1) || map.IsSemiSolid(x, y - 1))
+ {
+ map.SetTile(x, y - 1, 9);
+ map.SetTile(x, y, 0);
+ }
+ walkCD = TICKS_PAR_TILE;
+ }
+ if (GameState.keyState.isPressed(Control.DOWN) && walkCD is 0)
+ {
+ if (!map.IsSolid(x, y + 1) || map.IsSemiSolid(x, y + 1))
+ {
+ map.SetTile(x, y + 1, 9);
+ map.SetTile(x, y, 0);
+ }
+ walkCD = TICKS_PAR_TILE;
+ }
+ if (GameState.keyState.isPressed(Control.LEFT) && walkCD is 0)
+ {
+ if (!map.IsSolid(x - 1, y) || map.IsSemiSolid(x - 1, y))
+ {
+ map.SetTile(x - 1, y, 9);
+ map.SetTile(x, y, 0);
+ }
+ walkCD = TICKS_PAR_TILE;
+ }
+ if (GameState.keyState.isPressed(Control.RIGHT) && walkCD is 0)
+ {
+ if (!map.IsSolid(x + 1, y) || map.IsSemiSolid(x + 1, y))
+ {
+ map.SetTile(x + 1, y, 9);
+ map.SetTile(x, y, 0);
+ }
+ walkCD = TICKS_PAR_TILE;
+ }
+ walkCD = Math.Max(0, walkCD - 1);
+ base.Update(context, map, x, y);
+ }
+}
diff --git a/src/actors/Tiles/Rock.cs b/src/actors/Tiles/Rock.cs
new file mode 100644
index 0000000..374cfbe
--- /dev/null
+++ b/src/actors/Tiles/Rock.cs
@@ -0,0 +1,6 @@
+class Rock : Tile
+{
+ public Rock(TileSet tileSet, int srcX, int srcY) : base(tileSet, srcX, srcY)
+ {
+ }
+}
diff --git a/src/engine/Actor.cs b/src/engine/Actor.cs
index e38f6fd..5b6561b 100644
--- a/src/engine/Actor.cs
+++ b/src/engine/Actor.cs
@@ -1,6 +1,6 @@
abstract class Actor
{
- public virtual void Update(double dt, Context context)
+ public virtual void Update(Context context)
{
}
diff --git a/src/engine/GameState.cs b/src/engine/GameState.cs
index 0acdfd7..7445e56 100644
--- a/src/engine/GameState.cs
+++ b/src/engine/GameState.cs
@@ -3,4 +3,6 @@ static class GameState
public static KeyState keyState = new KeyState();
public static SceneManager sceneManager = new SceneManager();
public static int fps = 0;
+ public static int tick = 0;
+ public static bool saving = false;
}
diff --git a/src/engine/ResourceLoader.cs b/src/engine/ResourceLoader.cs
index f4b0077..0352418 100644
--- a/src/engine/ResourceLoader.cs
+++ b/src/engine/ResourceLoader.cs
@@ -11,6 +11,10 @@ class ResourceLoader
public string LoadString(string resourceName)
{
+#if (DEBUG)
+ Console.WriteLine("Loading resource: " + resourceName);
+ return File.ReadAllText(ToPath(resourceName));
+#endif
using var stream = this.GetType().Assembly.GetManifestResourceStream($"{this.assemblyName}.{resourceName}");
using var reader = new StreamReader(stream!);
return reader.ReadToEnd();
@@ -31,4 +35,16 @@ class ResourceLoader
var ptr = handle.AddrOfPinnedObject();
return (ptr, bytes.Length);
}
+
+ public void SaveString(string resourceName, string content)
+ {
+ using var stream = new StreamWriter(ToPath(resourceName));
+ stream.Write(content);
+ }
+
+ private static string ToPath(string resourceName)
+ {
+ var s = resourceName.Split('.');
+ return String.Join('/', s[..^1]) + "." + s[^1];
+ }
}
diff --git a/src/engine/Scene.cs b/src/engine/Scene.cs
index 740a890..9e34798 100644
--- a/src/engine/Scene.cs
+++ b/src/engine/Scene.cs
@@ -9,4 +9,14 @@ abstract class Scene : Actor
{
}
+
+ public virtual void OnMouseDown(Context context, byte button, int x, int y)
+ {
+
+ }
+
+ public virtual void OnKeyDown(Context context, SDL2.SDL.SDL_Keycode key, SDL2.SDL.SDL_Keymod mod)
+ {
+
+ }
}
diff --git a/src/scenes/Level.cs b/src/scenes/Level.cs
new file mode 100644
index 0000000..4d18571
--- /dev/null
+++ b/src/scenes/Level.cs
@@ -0,0 +1,94 @@
+enum TileType : int
+{
+ AIR = 0,
+ BORDER = 1,
+ DIRT = 2,
+ DIAMOND = 3,
+ ROCK = 4,
+ WALL = 5,
+ PLAYER = 9
+}
+
+class Level : Scene
+{
+ private Map? map;
+ private int debugSelectedTile = 0;
+ private TileSet? tileSet;
+ private Dictionary? tiles;
+
+ public override void Create(Context context)
+ {
+ var levelJson = context.resourceLoader.LoadString("assets.level1.json");
+ tiles = new Dictionary();
+ tileSet = new TileSet(context, "assets.sprites.png", 32);
+ tiles.Add((int)TileType.DIRT, new Dirt(tileSet, 1, 7));
+ tiles.Add((int)TileType.BORDER, new Tile(tileSet, 1, 6));
+ tiles.Add((int)TileType.DIAMOND, new Diamond(tileSet, 0, 10));
+ tiles.Add((int)TileType.ROCK, new Rock(tileSet, 0, 7));
+ tiles.Add((int)TileType.WALL, new Tile(tileSet, 3, 6));
+ tiles.Add((int)TileType.PLAYER, new Player(tileSet, 0, 0));
+ this.map = new Map(levelJson, tiles);
+ this.map.Dump();
+ }
+
+ public override void Draw(Context context)
+ {
+ this.map!.Draw(context);
+#if (DEBUG)
+ context.renderer.SetColor(0, 0, 0);
+ context.renderer.DrawRect(0, 0, 34, 34);
+ if (tiles?.ContainsKey(debugSelectedTile) == true)
+ {
+ tiles[debugSelectedTile]!.Draw(context, 0, 0);
+ }
+ else
+ {
+ context.renderer.SetColor(255, 0, 255);
+ context.renderer.DrawRect(0, 0, 32, 32);
+ }
+#endif
+ }
+
+ public override void Update(Context context)
+ {
+ this.map!.Update(context);
+ }
+
+ public override void OnMouseDown(Context context, byte button, int x, int y)
+ {
+ var tileX = x / 32;
+ var tileY = y / 32;
+ if (button == SDL2.SDL.SDL_BUTTON_LEFT)
+ {
+ this.map!.SetTile(tileX, tileY, this.debugSelectedTile);
+ }
+ if (button == SDL2.SDL.SDL_BUTTON_MIDDLE)
+ {
+ this.debugSelectedTile = this.map!.GetTile(tileX, tileY);
+ }
+ if (button == SDL2.SDL.SDL_BUTTON_RIGHT)
+ {
+ this.map!.SetTile(tileX, tileY, 0);
+ }
+ }
+
+ public override void OnKeyDown(Context context, SDL2.SDL.SDL_Keycode key, SDL2.SDL.SDL_Keymod mod)
+ {
+ var number = (int)key - (int)SDL2.SDL.SDL_Keycode.SDLK_0;
+ if (number >= 0 && number <= 9)
+ {
+ this.debugSelectedTile = number;
+ }
+ if (key == SDL2.SDL.SDL_Keycode.SDLK_e && mod == SDL2.SDL.SDL_Keymod.KMOD_LCTRL)
+ {
+ Console.WriteLine("Saving...");
+ var json = this.map!.ToJson();
+ context.resourceLoader.SaveString("assets.level1.json", json);
+ }
+ if (key == SDL2.SDL.SDL_Keycode.SDLK_r && mod == SDL2.SDL.SDL_Keymod.KMOD_LCTRL)
+ {
+ Console.WriteLine("Reloading...");
+ this.Create(context);
+ }
+ }
+}
diff --git a/src/scenes/MainMenu.cs b/src/scenes/MainMenu.cs
index 2eba468..a5fad7e 100644
--- a/src/scenes/MainMenu.cs
+++ b/src/scenes/MainMenu.cs
@@ -7,7 +7,7 @@ class MainMenu : Scene
context.fontManager.RegisterFont("MainMenu", "assets.font.ttf", 24);
}
- public override void Update(double dt, Context context)
+ public override void Update(Context context)
{
x += dir ? 10 : -10;
var windowSize = context.window.GetSize();
@@ -19,6 +19,10 @@ class MainMenu : Scene
{
dir = true;
}
+ if (GameState.keyState.isPressed(Control.CONFIRM))
+ {
+ GameState.sceneManager.ChangeScene("level", context);
+ }
}
public override void Draw(Context context)