From 7a51415e5e52ca4e31ad5ce32300a3afde56a5a3 Mon Sep 17 00:00:00 2001 From: Sander Schobers Date: Sun, 4 Jun 2023 19:23:37 +0200 Subject: [PATCH] Enabled tile randomization. --- src/engine.zig | 1 + src/engine/object.zig | 19 +++++++++++++++++++ src/game/game.zig | 23 +++++++++++++++++++++-- src/game/level.zig | 14 ++++++++------ src/game/tile_map.zig | 39 +++++++++++++++++++++++++-------------- src/game_scene.zig | 20 +++++++++++--------- 6 files changed, 85 insertions(+), 31 deletions(-) create mode 100644 src/engine/object.zig diff --git a/src/engine.zig b/src/engine.zig index db51883..8828591 100644 --- a/src/engine.zig +++ b/src/engine.zig @@ -3,6 +3,7 @@ const assets = @import("engine/assets.zig"); pub const Fonts = assets.Fonts; pub const FPS = @import("engine/fps.zig").FPS; pub const Keys = @import("engine/keys.zig").Keys; +pub const Object = @import("engine/object.zig").Object; pub const OpaquePtr = @import("engine/opaque_ptr.zig").OpaquePtr; pub const Point = @import("engine/point.zig").Point; pub const PointF = @import("engine/point_f.zig").PointF; diff --git a/src/engine/object.zig b/src/engine/object.zig new file mode 100644 index 0000000..2c7b75e --- /dev/null +++ b/src/engine/object.zig @@ -0,0 +1,19 @@ +const std = @import("std"); + +pub fn Object(comptime T: type) type { + return struct { + const Self = @This(); + + allocator: std.mem.Allocator, + ptr: *T, + + pub fn create(allocator: std.mem.Allocator) !Self { + const ptr = try allocator.create(T); + return Self{ .allocator = allocator, .ptr = ptr }; + } + + pub fn destroy(self: Self) void { + self.allocator.destroy(self.ptr); + } + }; +} diff --git a/src/game/game.zig b/src/game/game.zig index f50d06c..f16aba7 100644 --- a/src/game/game.zig +++ b/src/game/game.zig @@ -2,8 +2,9 @@ const std = @import("std"); const allegro = @import("allegro"); const engine = @import("../engine.zig"); const Animation = @import("animation.zig").Animation; -const Renderer = @import("../renderer.zig").Renderer; const Level = @import("level.zig").Level; +const Renderer = @import("../renderer.zig").Renderer; +const TileMap = @import("tile_map.zig").TileMap; pub const Game = struct { pub const Direction = enum { @@ -13,6 +14,8 @@ pub const Game = struct { pub const TileSize = 32; + prng: std.rand.DefaultPrng = std.rand.DefaultPrng.init(0), + level: Level, health: i64 = 4, @@ -25,10 +28,11 @@ pub const Game = struct { starAnimation: Animation, renderer: *Renderer, + randomTile: TileMap(usize), // current viewport translated to tiles. boundsInTiles: engine.RectangleF, - pub fn init(level: Level, renderer: *Renderer) Game { + pub fn init(allocator: std.mem.Allocator, level: Level, renderer: *Renderer) Game { const playerPosition = level.character.float(); return Game{ .level = level, @@ -39,10 +43,16 @@ pub const Game = struct { .isPlayerWalking = false, .starAnimation = Animation.init(renderer.sprites.get("item_star_32").?, 0.15), .renderer = renderer, + .randomTile = TileMap(usize).init(allocator), .boundsInTiles = Game.calculateBoundsInTiles(playerPosition), }; } + pub fn deinit(self: *Game) void { + self.level.deinit(); + self.randomTile.deinit(); + } + fn calculateBoundsInTiles(position: engine.PointF) engine.RectangleF { return engine.RectangleF.initRelative( position.x - 15, @@ -77,6 +87,15 @@ pub const Game = struct { self.boundsInTiles = Game.calculateBoundsInTiles(self.playerPosition); } + pub fn randomTileOffset(self: *Game, x: i64, y: i64, maxOffset: usize) usize { + if (self.randomTile.get(x, y)) |offset| { + return offset; + } + const offset = self.prng.random().intRangeLessThan(usize, 0, maxOffset); + self.randomTile.set(x, y, offset) catch {}; + return offset; + } + // return values are in view coordinates pub fn tile(self: Game, x: f32, y: f32) engine.PointF { return self.tileP(engine.PointF.init(x, y)); diff --git a/src/game/level.zig b/src/game/level.zig index bd4cc5b..eacafbe 100644 --- a/src/game/level.zig +++ b/src/game/level.zig @@ -15,7 +15,7 @@ pub const Level = struct { tiles: TileMap(Tile), collectables: TileMap(Collectable), - pub fn load(allocator: std.mem.Allocator, path: []const u8) !Level { + pub fn init(allocator: std.mem.Allocator, path: []const u8) !Level { const path_ = try std.fs.realpathAlloc(allocator, path); defer allocator.free(path_); @@ -25,10 +25,10 @@ pub const Level = struct { const data = try file.readToEndAlloc(allocator, 1024 * 1024); defer allocator.free(data); - return try Level.loadFromMemory(allocator, data); + return try Level.initFromMemory(allocator, data); } - pub fn loadFromMemory(allocator: std.mem.Allocator, data: []const u8) !Level { + pub fn initFromMemory(allocator: std.mem.Allocator, data: []const u8) !Level { const n = std.mem.count(u8, data, "\n"); _ = n; @@ -41,9 +41,6 @@ pub const Level = struct { while (lines.next()) |line| { defer y += 1; - try tiles.ensureColumns(line.len); - try collectables.ensureColumns(line.len); - for (line, 0..) |tile, column| { var x = @intCast(i64, column); switch (tile) { @@ -61,4 +58,9 @@ pub const Level = struct { .collectables = collectables, }; } + + pub fn deinit(self: *Level) void { + self.tiles.deinit(); + self.collectables.deinit(); + } }; diff --git a/src/game/tile_map.zig b/src/game/tile_map.zig index bca375d..dd54559 100644 --- a/src/game/tile_map.zig +++ b/src/game/tile_map.zig @@ -1,4 +1,5 @@ const std = @import("std"); +const Object = @import("../engine/object.zig").Object; pub fn TileMap(comptime Value: type) type { return struct { @@ -13,7 +14,7 @@ pub fn TileMap(comptime Value: type) type { }; } - pub fn deinit(self: Column) void { + pub fn deinit(self: *Column) void { self.values.deinit(); } @@ -27,35 +28,45 @@ pub fn TileMap(comptime Value: type) type { }; allocator: std.mem.Allocator, - columns: std.ArrayList(Column), + columns: std.AutoHashMap(i64, Column), pub fn init(allocator: std.mem.Allocator) Self { return Self{ .allocator = allocator, - .columns = std.ArrayList(Column).init(allocator), + .columns = std.AutoHashMap(i64, Column).init(allocator), }; } - pub fn deinit(self: Self) void { - for (self.columns.items) |item| { - item.deinit(); + pub fn deinit(self: *Self) void { + var columns = self.columns.valueIterator(); + while (columns.next()) |c| { + c.deinit(); } self.columns.deinit(); } - pub fn column(self: Self, i: i64) *Column { - return &self.columns.items[@intCast(usize, i)]; + pub fn column(self: Self, x: i64) *Column { + return self.columns.getPtr(x).?; } - pub fn ensureColumns(self: *Self, n: usize) !void { - while (self.columns.items.len < n) { - try self.columns.append(Column.init(self.allocator)); + pub fn ensureColumn(self: *Self, x: i64) !*Column { + if (self.columns.getPtr(x)) |c| { + return c; } + try self.columns.put(x, Column.init(self.allocator)); + return self.columns.getPtr(x).?; } - pub fn set(self: *Self, c: i64, r: i64, value: Value) !void { - try self.ensureColumns(@intCast(usize, c) + 1); - try self.column(c).set(r, value); + pub fn get(self: Self, x: i64, y: i64) ?Value { + if (self.columns.get(x)) |c| { + return c.get(y); + } + return null; + } + + pub fn set(self: *Self, x: i64, y: i64, value: Value) !void { + const c = try self.ensureColumn(x); + try c.set(y, value); } }; } diff --git a/src/game_scene.zig b/src/game_scene.zig index 57bfbd4..2c3506a 100644 --- a/src/game_scene.zig +++ b/src/game_scene.zig @@ -10,13 +10,13 @@ pub const GameScene = struct { game: game.Game = undefined, pub fn enter(self: *GameScene, ctx: *Context) void { - const level = game.Level.load(ctx.allocator, paths.AssetsDir ++ "/levels/level1.txt") catch unreachable; - self.game = game.Game.init(level, &ctx.renderer); + const level = game.Level.init(ctx.allocator, paths.AssetsDir ++ "/levels/level1.txt") catch unreachable; + self.game = game.Game.init(ctx.allocator, level, &ctx.renderer); } pub fn exit(self: *GameScene, ctx: *Context) void { + self.game.deinit(); _ = ctx; - _ = self; } pub fn handle(self: *GameScene, ctx: *Context, event: *allegro.Event) !void { @@ -68,20 +68,22 @@ pub const GameScene = struct { var x = tileBounds.min.x; while (x < tileBounds.max.x) : (x += 1) { - self.game.drawSpriteFrame("tiles_dirt_32", 0, x, 19); - self.game.drawSpriteFrame("tiles_dirt_32", 3, x, 20); + const randomDirtOffset = self.game.randomTileOffset(x, 99, 3); + self.game.drawSpriteFrame("tiles_dirt_32", 0 + randomDirtOffset, x, 19); + self.game.drawSpriteFrame("tiles_dirt_32", 3 + randomDirtOffset, x, 20); if (x < 0) continue; - const tiles = self.game.level.tiles.column(x); - const collectables = self.game.level.collectables.column(x); + const tiles = self.game.level.tiles.ensureColumn(x) catch continue; + const collectables = self.game.level.collectables.ensureColumn(x) catch continue; var y = tileBounds.min.y; while (y < tileBounds.max.y) : (y += 1) { if (tiles.get(y)) |tile| { switch (tile) { game.Level.Tile.Grass => { - self.game.drawSpriteFrame("tiles_grass_32", 1, x, y - 1); - self.game.drawSpriteFrame("tiles_grass_32", 5, x, y); + const randomOffset = self.game.randomTileOffset(x, y, 2); + self.game.drawSpriteFrame("tiles_grass_32", 1 + randomOffset, x, y - 1); + self.game.drawSpriteFrame("tiles_grass_32", 5 + randomOffset, x, y); }, // else => {}, }