const std = @import("std"); const engine = @import("../engine.zig"); pub const Level = struct { pub fn Column(comptime Value: type) type { return struct { const Self = @This(); values: std.AutoHashMap(i64, Value), pub fn init(allocator: std.mem.Allocator) Self { return Self{ .values = std.AutoHashMap(i64, Value).init(allocator), }; } pub fn deinit(self: Self) void { self.values.deinit(); } pub fn get(self: Self, row: i64) ?Value { return self.values.get(row); } pub fn set(self: *Self, row: i64, value: Value) !void { try self.values.put(row, value); } }; } pub fn Components(comptime Value: type) type { return struct { const Self = @This(); allocator: std.mem.Allocator, columns: std.ArrayList(Column(Value)), pub fn init(allocator: std.mem.Allocator) Self { return Self{ .allocator = allocator, .columns = std.ArrayList(Column(Value)).init(allocator), }; } pub fn deinit(self: Self) void { for (self.columns.items) |item| { item.deinit(); } self.columns.deinit(); } pub fn column(self: Self, i: i64) *Column(Value) { return &self.columns.items[@intCast(usize, i)]; } fn ensureColumns(self: *Self, n: usize) !void { while (self.columns.items.len < n) { try self.columns.append(Column(Value).init(self.allocator)); } } 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 const Tile = enum { Grass, }; pub const Collectable = enum { Star, }; character: engine.Point, tiles: Components(Tile), collectables: Components(Collectable), pub fn load(allocator: std.mem.Allocator, path: []const u8) !Level { const path_ = try std.fs.realpathAlloc(allocator, path); defer allocator.free(path_); const file = try std.fs.openFileAbsolute(path_, .{ .mode = .read_only }); defer file.close(); const data = try file.readToEndAlloc(allocator, 1024 * 1024); defer allocator.free(data); return try Level.loadFromMemory(allocator, data); } pub fn loadFromMemory(allocator: std.mem.Allocator, data: []const u8) !Level { const n = std.mem.count(u8, data, "\n"); _ = n; var character: ?engine.Point = null; var tiles = Components(Tile).init(allocator); var collectables = Components(Collectable).init(allocator); var lines = std.mem.split(u8, data, "\n"); var y: i64 = 0; 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) { 'P' => character = engine.Point.init(x, y), 'S' => try collectables.set(x, y, Collectable.Star), 'x' => try tiles.set(x, y, Tile.Grass), else => {}, } } } return Level{ .character = character.?, .tiles = tiles, .collectables = collectables, }; } };