diff --git a/src/world.zig b/src/world.zig index 24cacd2..4db704c 100644 --- a/src/world.zig +++ b/src/world.zig @@ -1,5 +1,7 @@ //! Data types used for storing world info +const std = @import("std"); + // Tile Storage Types pub const CircuitType = enum(u4) { None = 0, @@ -14,24 +16,77 @@ pub const CircuitType = enum(u4) { Source = 9, }; -pub const TileData = packed union { +pub const TileData = union { tile: u7, - flags: packed struct { + flags: struct { solid: bool, circuit: u4, }, }; -pub const TileStore = packed struct { +pub const TileStore = struct { is_tile: bool, data: TileData, + + pub fn toByte(store: TileStore) u8 { + if (store.is_tile) { + return 1 | (store.data.tile << 1); + } else { + return 0 | (@intCast(u2, @boolToInt(store.data.flags.solid)) << 1) | (store.data.flags.circuit << 2); + } + } + + pub fn fromByte(byte: u8) TileStore { + const is_tile = (1 & byte) > 0; + if (is_tile) { + const tile = @intCast(u7, (~1 & byte) >> 1); + return TileStore{ + .is_tile = is_tile, + .data = .{ .tile = tile }, + }; + } else { + const is_solid = (0b0000_0010 & byte) > 0; + const circuit = @intCast(u4, (0b0011_1100 & byte) >> 2); + return TileStore{ + .is_tile = is_tile, + .data = .{ .flags = .{ + .solid = is_solid, + .circuit = circuit, + } }, + }; + } + } }; pub const LevelHeader = struct { world_x: u8, world_y: u8, width: u16, - size: u16, + size: u16, + + pub fn write(header: LevelHeader, writer: anytype) !void { + try writer.writeInt(u8, header.world_x, .Big); + try writer.writeInt(u8, header.world_y, .Big); + try writer.writeInt(u16, header.width, .Big); + try writer.writeInt(u16, header.size, .Big); + } + + pub fn read(reader: anytype) !LevelHeader { + return LevelHeader{ + .world_x = try reader.readInt(u8, .Big), + .world_y = try reader.readInt(u8, .Big), + .width = try reader.readInt(u16, .Big), + .size = try reader.readInt(u16, .Big), + }; + } + + pub fn readTiles(header: LevelHeader, reader: anytype, buf: []TileStore) !void { + std.debug.assert(buf.len > header.size); + var i: usize = 0; + while (i < header.size) : (i += 1) { + buf[i] = TileStore.fromByte(reader.readByte()); + } + } }; pub const Level = struct { diff --git a/tools/LDtkImport.zig b/tools/LDtkImport.zig index 7d03788..41fe15b 100644 --- a/tools/LDtkImport.zig +++ b/tools/LDtkImport.zig @@ -47,7 +47,6 @@ fn make(step: *std.build.Step) !void { var data = std.ArrayList(u8).init(allocator); defer data.deinit(); const writer = data.writer(); - _ = writer; // TODO: Convert LDtk data into wired format const ldtk = try LDtk.parse(allocator, source); @@ -56,58 +55,78 @@ fn make(step: *std.build.Step) !void { if (ldtk.levels.len > 0) { const level0 = ldtk.levels[0]; if (level0.layerInstances) |layers| { - var circuit: []const u8 = "null"; - var collision: []const u8 = "null"; + const world_x: u8 = @intCast(u8, @divExact(level0.worldX, (ldtk.worldGridWidth orelse 160))); + const world_y: u8 = @intCast(u8, @divExact(level0.worldY, (ldtk.worldGridHeight orelse 160))); + + var circuit_layer: ?LDtk.LayerInstance = null; + var collision_layer: ?LDtk.LayerInstance = null; for (layers) |layer| { if (std.mem.eql(u8, layer.__identifier, "Entities")) { std.debug.assert(layer.__type == .Entities); for (layer.entityInstances) |entity| { std.log.warn("{s}", .{entity.__identifier}); } - } - else if (std.mem.eql(u8, layer.__identifier, "Circuit")) { + } else if (std.mem.eql(u8, layer.__identifier, "Circuit")) { std.debug.assert(layer.__type == .IntGrid); - var grid_str = try allocator.alloc(u8, @intCast(usize, layer.__cWid * layer.__cHei + layer.__cHei)); - defer allocator.free(grid_str); - var i: usize = 0; - var o: usize = 0; - for (layer.intGridCsv) |int| { - grid_str[i + o] = std.fmt.digitToChar(@intCast(u8, int), .lower); - if (grid_str[i] == '0') grid_str[i] = ' '; - i += 1; - if (@mod(i, @intCast(usize, layer.__cWid)) == 0) { - grid_str[i + o] = '\n'; - o += 1; - } - } - - circuit = grid_str; - } - else if (std.mem.eql(u8, layer.__identifier, "Collision")) { + circuit_layer = layer; + } else if (std.mem.eql(u8, layer.__identifier, "Collision")) { std.debug.assert(layer.__type == .IntGrid); - var grid_str = try allocator.alloc(u8, @intCast(usize, layer.__cWid * layer.__cHei + layer.__cHei)); - var i: usize = 0; - var o: usize = 0; - for (layer.intGridCsv) |int| { - grid_str[i + o] = std.fmt.digitToChar(@intCast(u8, int), .lower); - if (grid_str[i] == '0') grid_str[i] = ' '; - i += 1; - if (@mod(i, @intCast(usize, layer.__cWid)) == 0) { - grid_str[i + o] = '\n'; - o += 1; - } - } - - collision = grid_str; + collision_layer = layer; } else { std.log.warn("{s}: {}", .{ layer.__identifier, layer.__type }); } } - std.log.warn("Circuit IntGrid:\n{s}\nCollision IntGrid:\n{s}", .{ circuit, collision}); - allocator.free(circuit); - allocator.free(collision); + + if (circuit_layer == null) return error.MissingCircuitLayer; + if (collision_layer == null) return error.MissingCollisionLayer; + + std.log.warn("Layers found", .{}); + + const circuit = circuit_layer.?; + const collision = collision_layer.?; + + std.debug.assert(circuit.__cWid == collision.__cWid); + std.debug.assert(circuit.__cHei == collision.__cHei); + + const width = @intCast(u16, circuit.__cWid); + const size = @intCast(u16, width * circuit.__cHei); + + try (world.LevelHeader{ + .world_x = world_x, + .world_y = world_y, + .width = @intCast(u16, width), + .size = @intCast(u16, size), + }).write(writer); + + var tiles = try allocator.alloc(world.TileStore, size); + defer allocator.free(tiles); + + for (collision.autoLayerTiles) |autotile| { + const x = @divExact(autotile.px[0], collision.__gridSize); + const y = @divExact(autotile.px[1], collision.__gridSize); + const i = @intCast(usize, x + y * width); + tiles[i] = world.TileStore{ + .is_tile = true, + .data = .{ .tile = @intCast(u7, autotile.t) }, + }; + } + + for (circuit.intGridCsv) |cir64, i| { + const cir = @intCast(u4, cir64); + const col = collision.intGridCsv[i]; + tiles[i] = world.TileStore{ + .is_tile = false, + .data = .{ + .flags = .{ + .solid = col == 1, + .circuit = cir, + }, + }, + }; + try writer.writeByte(tiles[i].toByte()); + } } }