From 33ebdd2c8df70bb0be6670042feb99cb3f11d62c Mon Sep 17 00:00:00 2001 From: MasterGordon Date: Wed, 12 Oct 2022 01:34:58 +0200 Subject: [PATCH] added level + editor --- assets/level1.json | 19 +----- boulder-dash.csproj | 1 - src/BoulderDash.cs | 16 +++-- src/Controls.cs | 3 + src/actors/Map.cs | 121 +++++++++++++++++++++++++++++++++-- src/actors/Tile.cs | 31 +++++++++ src/actors/Tiles/Diamond.cs | 28 ++++++++ src/actors/Tiles/Dirt.cs | 11 ++++ src/actors/Tiles/Player.cs | 56 ++++++++++++++++ src/actors/Tiles/Rock.cs | 6 ++ src/engine/Actor.cs | 2 +- src/engine/GameState.cs | 2 + src/engine/ResourceLoader.cs | 16 +++++ src/engine/Scene.cs | 10 +++ src/scenes/Level.cs | 94 +++++++++++++++++++++++++++ src/scenes/MainMenu.cs | 6 +- 16 files changed, 390 insertions(+), 32 deletions(-) create mode 100644 src/actors/Tile.cs create mode 100644 src/actors/Tiles/Diamond.cs create mode 100644 src/actors/Tiles/Dirt.cs create mode 100644 src/actors/Tiles/Player.cs create mode 100644 src/actors/Tiles/Rock.cs create mode 100644 src/scenes/Level.cs 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)