Begin converting game.zig to use ldtk data
parent
b5ca48e1b3
commit
84b5e4cb41
|
@ -1 +1 @@
|
|||
Subproject commit 91d78d9c52f4f00f906b89bad8ff27fe295dd1f6
|
||||
Subproject commit e93618602d79d1e998a10c0ce1b4473459b19ff4
|
|
@ -234,17 +234,17 @@ pub fn enable(this: *@This(), cell: Cell) void {
|
|||
this.levels[i] += 1;
|
||||
}
|
||||
|
||||
pub fn bridge(this: *@This(), cells: [2]Cell, bridgeID: usize) !void {
|
||||
pub fn bridge(this: *@This(), cells: [2]Cell, bridgeID: usize) void {
|
||||
if (this.indexOf(cells[0])) |_| {
|
||||
if (this.indexOf(cells[1])) |_| {
|
||||
try this.bridges.append(.{ .cells = cells, .id = bridgeID, .enabled = false });
|
||||
this.bridges.append(.{ .cells = cells, .id = bridgeID, .enabled = false });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn addSource(this: *@This(), cell: Cell) !void {
|
||||
pub fn addSource(this: *@This(), cell: Cell) void {
|
||||
if (this.indexOf(cell)) |_| {
|
||||
try this.sources.append(cell);
|
||||
this.sources.append(cell);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -265,7 +265,7 @@ pub fn enabledBridges(this: @This(), alloc: std.mem.Allocator) !util.Buffer(usiz
|
|||
|
||||
pub fn enabledDoors(this: @This(), alloc: std.mem.Allocator) !util.Buffer(Cell) {
|
||||
var items = try alloc.alloc(Cell, this.doors.len);
|
||||
var buffer = util.buffer(Cell).init(items);
|
||||
var buffer = util.Buffer(Cell).init(items);
|
||||
for (this.doors.items) |d| {
|
||||
if (d.enabled) buffer.append(d.cell);
|
||||
}
|
||||
|
@ -289,11 +289,10 @@ pub fn toggle(this: *@This(), c: Cell) void {
|
|||
|
||||
pub fn clear(this: *@This()) void {
|
||||
std.mem.set(u8, this.levels, 0);
|
||||
for (this.doors.slice()) |*door| {
|
||||
for (this.doors.items) |*door| {
|
||||
door.enabled = false;
|
||||
}
|
||||
// Resizing to zero should always work
|
||||
this.bridges.resize(0) catch unreachable;
|
||||
this.bridges.reset();
|
||||
}
|
||||
|
||||
pub fn reset(this: *@This()) void {
|
||||
|
@ -303,7 +302,7 @@ pub fn reset(this: *@This()) void {
|
|||
}
|
||||
|
||||
const w4 = @import("wasm4.zig");
|
||||
const Queue = util.Queue(Cell, MAXCELLS);
|
||||
const Queue = util.Queue(Cell);
|
||||
// Returns number of cells filled
|
||||
pub fn fill(this: *@This(), alloc: std.mem.Allocator) !usize {
|
||||
var count: usize = 0;
|
||||
|
@ -313,13 +312,15 @@ pub fn fill(this: *@This(), alloc: std.mem.Allocator) !usize {
|
|||
|
||||
var visited = util.Buffer(usize).init(items);
|
||||
|
||||
var q = try Queue.init();
|
||||
for (this.sources.slice()) |source| {
|
||||
var q_buf = try alloc.alloc(Cell, MAXCELLS);
|
||||
var q = Queue.init(q_buf);
|
||||
|
||||
for (this.sources.items) |source| {
|
||||
try q.insert(source);
|
||||
}
|
||||
while (q.remove()) |cell| {
|
||||
const tile = this.get_cell(cell) orelse {
|
||||
for (this.doors.slice()) |*d| {
|
||||
for (this.doors.items) |*d| {
|
||||
if (@reduce(.And, d.cell == cell)) {
|
||||
d.enabled = true;
|
||||
}
|
||||
|
@ -328,9 +329,9 @@ pub fn fill(this: *@This(), alloc: std.mem.Allocator) !usize {
|
|||
};
|
||||
const index = this.indexOf(cell) orelse continue;
|
||||
this.enable(cell);
|
||||
const hasVisited = std.mem.containsAtLeast(usize, visited.slice(), 1, &.{index});
|
||||
const hasVisited = std.mem.containsAtLeast(usize, visited.items, 1, &.{index});
|
||||
if (hasVisited and !is_logic(tile)) continue;
|
||||
try visited.append(index);
|
||||
visited.append(index);
|
||||
count += 1;
|
||||
if (get_logic(tile)) |logic| {
|
||||
// TODO: implement other logic (though I'm pretty sure that requires a graph...)
|
||||
|
@ -349,7 +350,7 @@ pub fn fill(this: *@This(), alloc: std.mem.Allocator) !usize {
|
|||
nextCell[1] >= this.map_size[1])
|
||||
continue;
|
||||
const nextTile = this.get_cell(nextCell) orelse here: {
|
||||
for (this.doors.slice()) |*d| {
|
||||
for (this.doors.items) |*d| {
|
||||
if (@reduce(.And, d.cell == nextCell)) {
|
||||
d.enabled = true;
|
||||
}
|
||||
|
@ -360,7 +361,7 @@ pub fn fill(this: *@This(), alloc: std.mem.Allocator) !usize {
|
|||
try q.insert(nextCell);
|
||||
}
|
||||
if (is_plug(tile)) {
|
||||
for (this.bridges.slice()) |*b| {
|
||||
for (this.bridges.items) |*b| {
|
||||
if (@reduce(.And, b.cells[0] == cell)) {
|
||||
try q.insert(b.cells[1]);
|
||||
b.enabled = true;
|
||||
|
|
110
src/game.zig
110
src/game.zig
|
@ -1,6 +1,5 @@
|
|||
const std = @import("std");
|
||||
const w4 = @import("wasm4.zig");
|
||||
const assets = @import("assets");
|
||||
const input = @import("input.zig");
|
||||
const util = @import("util.zig");
|
||||
const Circuit = @import("circuit.zig");
|
||||
|
@ -8,6 +7,9 @@ const Map = @import("map.zig");
|
|||
const Music = @import("music.zig");
|
||||
const State = @import("main.zig").State;
|
||||
const Disk = @import("disk.zig");
|
||||
const extract = @import("extract.zig");
|
||||
const world = @import("world.zig");
|
||||
const world_data = @import("world_data");
|
||||
|
||||
const Vec2 = util.Vec2;
|
||||
const Vec2f = util.Vec2f;
|
||||
|
@ -136,6 +138,15 @@ fn randRangeF(min: f32, max: f32) f32 {
|
|||
return min + (random.float(f32) * (max - min));
|
||||
}
|
||||
|
||||
// Allocators
|
||||
var fba_buf: [4096]u8 = undefined;
|
||||
var fba = std.heap.FixedBufferAllocator.init(&fba_buf);
|
||||
var alloc = fba.allocator();
|
||||
|
||||
var frame_fba_buf: [4096]u8 = undefined;
|
||||
var frame_fba = std.heap.FixedBufferAllocator.init(&frame_fba_buf);
|
||||
var frame_alloc = frame_fba.allocator();
|
||||
|
||||
// Global vars
|
||||
var map: Map = undefined;
|
||||
var circuit: Circuit = undefined;
|
||||
|
@ -156,9 +167,12 @@ var ScoreCoin = Sprite{
|
|||
.flags = .{ .bpp = .b2 },
|
||||
};
|
||||
|
||||
var solids_mutable = assets.solid;
|
||||
pub var conduit_mutable = assets.conduit;
|
||||
var conduitLevels_mutable: [conduit_mutable.len]u8 = undefined;
|
||||
var map_buf: [400]u8 = undefined;
|
||||
|
||||
var circuit_lvl_buf: [400]u8 = undefined;
|
||||
var circuit_buf: [400]u8 = undefined;
|
||||
|
||||
var circuit_options: Circuit.Options = undefined;
|
||||
|
||||
pub const anim_store = struct {
|
||||
const stand = Anim.frame(8);
|
||||
|
@ -187,17 +201,66 @@ fn showErr(msg: []const u8) noreturn {
|
|||
pub fn start() !void {
|
||||
particles = try ParticleSystem.init();
|
||||
|
||||
std.mem.set(u8, &conduitLevels_mutable, 0);
|
||||
circuit = try Circuit.init(&conduit_mutable, &conduitLevels_mutable, assets.conduit_size);
|
||||
map = Map.init(&solids_mutable, assets.solid_size);
|
||||
var level_size = Vec2{ 20, 20 };
|
||||
|
||||
camera = @divTrunc(assets.spawn, @splat(2, @as(i32, 20))) * @splat(2, @as(i32, 20));
|
||||
circuit_options = .{
|
||||
.map = &circuit_buf,
|
||||
.levels = &circuit_lvl_buf,
|
||||
.map_size = level_size,
|
||||
.bridges = try alloc.alloc(Circuit.BridgeState, 5),
|
||||
.sources = try alloc.alloc(util.Cell, 5),
|
||||
.doors = try alloc.alloc(Circuit.DoorState, 5),
|
||||
};
|
||||
circuit = Circuit.init(circuit_options);
|
||||
|
||||
map = Map.init(&map_buf, level_size);
|
||||
|
||||
var stream = std.io.FixedBufferStream([]const u8){
|
||||
.pos = 0,
|
||||
.buffer = world_data,
|
||||
};
|
||||
const world_reader = stream.reader();
|
||||
|
||||
var level = try world.Level.read(world_reader);
|
||||
var level_buf = try alloc.alloc(world.TileData, level.size);
|
||||
try level.readTiles(world_reader, level_buf);
|
||||
|
||||
try extract.extractLevel(.{
|
||||
.alloc = frame_alloc,
|
||||
.level = level,
|
||||
.map = &map,
|
||||
.circuit = &circuit,
|
||||
.tileset = world.AutoTileset.initOffsetFull(113),
|
||||
.conduit = world.AutoTileset.initOffsetFull(97),
|
||||
.plug = world.AutoTileset.initOffsetCardinal(17),
|
||||
.switch_off = world.AutoTileset.initSwitches(&.{
|
||||
29, // South-North
|
||||
25, // South-West-North
|
||||
27, // South-East-North
|
||||
}, 2),
|
||||
.switch_on = world.AutoTileset.initSwitches(&.{
|
||||
30, // South-North
|
||||
26, // South-West-North
|
||||
28, // South-East-West
|
||||
}, 2),
|
||||
});
|
||||
|
||||
var entity_buf = try alloc.alloc(world.Entity, level.entity_count);
|
||||
try level.readEntities(world_reader, entity_buf);
|
||||
|
||||
const spawnArr = level.getSpawn().?;
|
||||
const spawn = Vec2{ spawnArr[0], spawnArr[1] };
|
||||
// std.mem.set(u8, &conduitLevels_mutable, 0);
|
||||
// circuit = try Circuit.init(&conduit_mutable, &conduitLevels_mutable, assets.conduit_size);
|
||||
// map = Map.init(&solids_mutable, assets.solid_size);
|
||||
|
||||
camera = @divTrunc(spawn, @splat(2, @as(i32, 20))) * @splat(2, @as(i32, 20));
|
||||
|
||||
const tile_size = Vec2{ 8, 8 };
|
||||
const offset = Vec2{ 4, 8 };
|
||||
|
||||
player = .{
|
||||
.pos = Pos.init(util.vec2ToVec2f(assets.spawn * tile_size + offset)),
|
||||
.pos = Pos.init(util.vec2ToVec2f(spawn * tile_size + offset)),
|
||||
.control = .{ .controller = .player, .state = .stand },
|
||||
.sprite = .{ .offset = .{ -4, -8 }, .size = .{ 8, 8 }, .index = 8, .flags = .{ .bpp = .b2 } },
|
||||
.physics = .{ .friction = Vec2f{ 0.15, 0.1 }, .gravity = Vec2f{ 0, 0.25 } },
|
||||
|
@ -210,12 +273,12 @@ pub fn start() !void {
|
|||
|
||||
_ = try wires.resize(0);
|
||||
for (assets.wire) |wire| {
|
||||
var w = wires.addOne() catch showErr("New wire");
|
||||
var w = try wires.addOne();
|
||||
_ = try w.nodes.resize(0);
|
||||
const divisions = wire.divisions;
|
||||
var i: usize = 0;
|
||||
while (i <= divisions) : (i += 1) {
|
||||
w.nodes.append(Pos.init(Vec2f{ 0, 0 })) catch showErr("Appending nodes");
|
||||
try w.nodes.append(Pos.init(Vec2f{ 0, 0 }));
|
||||
}
|
||||
w.begin().pos = util.vec2ToVec2f(wire.p1);
|
||||
w.end().pos = util.vec2ToVec2f(wire.p2);
|
||||
|
@ -237,12 +300,12 @@ pub fn start() !void {
|
|||
try coins.resize(0);
|
||||
if (!try Disk.load()) {
|
||||
for (assets.coins) |coin| {
|
||||
coins.append(.{
|
||||
try coins.append(.{
|
||||
.pos = Pos.init(util.vec2ToVec2f(coin * tile_size)),
|
||||
.sprite = .{ .offset = .{ 0, 0 }, .size = .{ 8, 8 }, .index = 4, .flags = .{ .bpp = .b2 } },
|
||||
.anim = Anim{ .anim = &anim_store.coin },
|
||||
.area = .{ .pos = .{ 0, 0 }, .size = .{ 8, 8 } },
|
||||
}) catch showErr("Appending coin");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -252,6 +315,9 @@ pub fn start() !void {
|
|||
var indicator: ?Interaction = null;
|
||||
|
||||
pub fn update(time: usize) !State {
|
||||
// Clear the frame buffer
|
||||
frame_fba.reset();
|
||||
|
||||
for (wires.slice()) |*wire| {
|
||||
try wirePhysicsProcess(1, wire);
|
||||
if (wire.enabled) {
|
||||
|
@ -387,8 +453,8 @@ pub fn update(time: usize) !State {
|
|||
}
|
||||
|
||||
// Music
|
||||
const musicCommand = try music.getNext(1);
|
||||
for (musicCommand.constSlice()) |sfx| {
|
||||
const musicCommand = try music.getNext(1, frame_alloc);
|
||||
for (musicCommand.items) |sfx| {
|
||||
w4.tone(sfx.freq, sfx.duration, sfx.volume, sfx.flags);
|
||||
}
|
||||
|
||||
|
@ -539,9 +605,9 @@ fn updateCircuit() !void {
|
|||
const cellBegin = util.world2cell(nodes[0].pos);
|
||||
const cellEnd = util.world2cell(nodes[nodes.len - 1].pos);
|
||||
|
||||
try circuit.bridge(.{ cellBegin, cellEnd }, wireID);
|
||||
circuit.bridge(.{ cellBegin, cellEnd }, wireID);
|
||||
}
|
||||
_ = try circuit.fill();
|
||||
_ = try circuit.fill(frame_alloc);
|
||||
for (wires.slice()) |*wire| {
|
||||
const begin = wire.begin();
|
||||
const end = wire.end();
|
||||
|
@ -551,8 +617,8 @@ fn updateCircuit() !void {
|
|||
(circuit.isEnabled(cellEnd) and end.pinned)) wire.enabled = true;
|
||||
}
|
||||
map.reset(&assets.solid);
|
||||
const enabledDoors = try circuit.enabledDoors();
|
||||
for (enabledDoors.constSlice()) |door| {
|
||||
const enabledDoors = try circuit.enabledDoors(frame_alloc);
|
||||
for (enabledDoors.items) |door| {
|
||||
try map.set_cell(door, 0);
|
||||
}
|
||||
}
|
||||
|
@ -698,7 +764,7 @@ fn controlProcess(_: f32, pos: *Pos, control: *Control, physics: *Physics, kinem
|
|||
fn kinematicProcess(_: f32, pos: *Pos, kinematic: *Kinematic) !void {
|
||||
var next = pos.last;
|
||||
next[0] = pos.pos[0];
|
||||
var hcol = try map.collide(kinematic.col.addv(next));
|
||||
var hcol = map.collide(kinematic.col.addv(next));
|
||||
if (hcol.len > 0) {
|
||||
kinematic.lastCol[0] = next[0] - pos.last[0];
|
||||
next[0] = pos.last[0];
|
||||
|
@ -707,7 +773,7 @@ fn kinematicProcess(_: f32, pos: *Pos, kinematic: *Kinematic) !void {
|
|||
}
|
||||
|
||||
next[1] = pos.pos[1];
|
||||
var vcol = try map.collide(kinematic.col.addv(next));
|
||||
var vcol = map.collide(kinematic.col.addv(next));
|
||||
if (vcol.len > 0) {
|
||||
kinematic.lastCol[1] = next[1] - pos.last[1];
|
||||
next[1] = pos.last[1];
|
||||
|
@ -716,7 +782,7 @@ fn kinematicProcess(_: f32, pos: *Pos, kinematic: *Kinematic) !void {
|
|||
}
|
||||
|
||||
var colPosAbs = next + kinematic.lastCol;
|
||||
var lastCol = try map.collide(kinematic.col.addv(colPosAbs));
|
||||
var lastCol = map.collide(kinematic.col.addv(colPosAbs));
|
||||
if (lastCol.len == 0) {
|
||||
kinematic.lastCol = Vec2f{ 0, 0 };
|
||||
}
|
||||
|
|
11
src/main.zig
11
src/main.zig
|
@ -4,7 +4,7 @@ const assets = @import("assets");
|
|||
const input = @import("input.zig");
|
||||
const util = @import("util.zig");
|
||||
|
||||
const game = @import("rewrite.zig");
|
||||
const game = @import("game.zig");
|
||||
const menu = @import("menu.zig");
|
||||
|
||||
pub const State = enum {
|
||||
|
@ -29,8 +29,9 @@ export fn update() void {
|
|||
.Menu => menu.update(),
|
||||
.Game => game.update(time) catch |e| switch (e) {
|
||||
error.Overflow => showErr(@errorName(e)),
|
||||
// error.OutOfBounds => showErr(@errorName(e)),
|
||||
error.EndOfStream => showErr(@errorName(e)),
|
||||
error.OutOfBounds => showErr(@errorName(e)),
|
||||
// error.EndOfStream => showErr(@errorName(e)),
|
||||
error.OutOfMemory => showErr(@errorName(e)),
|
||||
},
|
||||
};
|
||||
if (state != newState) {
|
||||
|
@ -39,8 +40,8 @@ export fn update() void {
|
|||
.Menu => menu.start(),
|
||||
.Game => game.start() catch |e| switch (e) {
|
||||
// error.Overflow => showErr(@errorName(e)),
|
||||
// error.OutOfBounds => showErr(@errorName(e)),
|
||||
error.EndOfStream => showErr(@errorName(e)),
|
||||
error.OutOfBounds => showErr(@errorName(e)),
|
||||
// error.EndOfStream => showErr(@errorName(e)),
|
||||
error.OutOfMemory => showErr(@errorName(e)),
|
||||
error.NullTiles => showErr(@errorName(e)),
|
||||
},
|
||||
|
|
|
@ -118,7 +118,7 @@ pub const CollisionInfo = struct {
|
|||
};
|
||||
}
|
||||
|
||||
pub fn append(col: CollisionInfo, item: util.AABB) void {
|
||||
pub fn append(col: *CollisionInfo, item: util.AABB) void {
|
||||
std.debug.assert(col.len < 9);
|
||||
col.items[col.len] = item;
|
||||
col.len += 1;
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
const std = @import("std");
|
||||
const util = @import("util.zig");
|
||||
const w4 = @import("wasm4.zig");
|
||||
|
||||
// Adapted from https://gist.github.com/YuxiUx/c3a8787209e32fc29fb48e8454f0009c
|
||||
|
@ -89,9 +90,10 @@ pub const Procedural = struct {
|
|||
this.collect = .{ .score = score, .start = beatTotal + 1, .end = beatTotal + (this.beatsPerBar * length) + 1 };
|
||||
}
|
||||
|
||||
pub fn getNext(this: *@This(), dt: u32) MusicCommand {
|
||||
var i = 0;
|
||||
var cmd: [4]Sfx = undefined;
|
||||
pub fn getNext(this: *@This(), dt: u32, alloc: std.mem.Allocator) !util.Buffer(Sfx) {
|
||||
var sfx_buf = try alloc.alloc(Sfx, 4);
|
||||
var cmd = util.Buffer(Sfx).init(sfx_buf);
|
||||
|
||||
const beatProgress = this.tick % this.beat;
|
||||
const beatTotal = @divTrunc(this.tick, this.beat);
|
||||
const beat = beatTotal % this.beatsPerBar;
|
||||
|
@ -102,13 +104,12 @@ pub const Procedural = struct {
|
|||
const playNote = if (collect.score < 6) beat % 2 == 0 else beat % 4 != 3;
|
||||
if (beatTotal >= collect.start and beatTotal < collect.end and playNote and beatProgress == 0) {
|
||||
// const notelen = @intCast(u8, this.beat * this.beatsPerBar);
|
||||
cmd[i] = (Sfx{
|
||||
cmd.append(Sfx{
|
||||
.freq = .{ .start = this.nextNote(this.note) },
|
||||
.duration = .{ .sustain = 5, .release = 5 },
|
||||
.volume = 25,
|
||||
.flags = .{ .channel = .pulse2, .mode = .p25 },
|
||||
});
|
||||
i += 1;
|
||||
this.note += 1;
|
||||
}
|
||||
if (bar > collect.end) {
|
||||
|
@ -117,31 +118,28 @@ pub const Procedural = struct {
|
|||
}
|
||||
}
|
||||
if (this.intensity.atLeast(.calm) and beat == 0 and beatProgress == 0) {
|
||||
cmd[i] = (.{
|
||||
cmd.append (.{
|
||||
.freq = .{ .start = 220, .end = 110 },
|
||||
.duration = .{ .release = 3 },
|
||||
.volume = 100,
|
||||
.flags = .{ .channel = .triangle },
|
||||
});
|
||||
i += 1;
|
||||
}
|
||||
if (this.intensity.atLeast(.active) and beat == this.beatsPerBar / 2 and beatProgress == 0) {
|
||||
cmd[i] = (.{
|
||||
cmd.append(.{
|
||||
.freq = .{ .start = 110, .end = 55 },
|
||||
.duration = .{ .release = 3 },
|
||||
.volume = 100,
|
||||
.flags = .{ .channel = .triangle },
|
||||
});
|
||||
i += 1;
|
||||
}
|
||||
if (this.walking and beat % 3 == 1 and beatProgress == 7) {
|
||||
cmd[i] = (.{
|
||||
cmd.append(.{
|
||||
.freq = .{ .start = 1761, .end = 1 },
|
||||
.duration = .{ .release = 5 },
|
||||
.volume = 25,
|
||||
.flags = .{ .channel = .noise },
|
||||
});
|
||||
i += 1;
|
||||
}
|
||||
return cmd;
|
||||
}
|
||||
|
|
|
@ -117,11 +117,11 @@ pub fn Buffer(comptime T: type) type {
|
|||
};
|
||||
}
|
||||
|
||||
pub fn reset(buf: @This()) void {
|
||||
pub fn reset(buf: *@This()) void {
|
||||
buf.len = 0;
|
||||
}
|
||||
|
||||
pub fn append(buf: @This(), item: T) void {
|
||||
pub fn append(buf: *@This(), item: T) void {
|
||||
std.debug.assert(buf.len < buf.items.len);
|
||||
buf.items[buf.len] = item;
|
||||
buf.len += 1;
|
||||
|
|
|
@ -2,7 +2,8 @@
|
|||
|
||||
const std = @import("std");
|
||||
|
||||
// Tile Storage Types
|
||||
/// The CircuitType of a tile modifies how the tile responds to
|
||||
/// electricity
|
||||
pub const CircuitType = enum(u4) {
|
||||
None = 0,
|
||||
Conduit = 1,
|
||||
|
@ -51,36 +52,48 @@ pub const Level = struct {
|
|||
world_y: u8,
|
||||
width: u16,
|
||||
size: u16,
|
||||
entity_count: u16,
|
||||
tiles: ?[]TileData,
|
||||
entities: ?[]Entity = null,
|
||||
|
||||
pub fn init(x: u8, y: u8, width: u16, buf: []TileData) Level {
|
||||
pub fn init(x: u8, y: u8, width: u16, buf: []TileData, entities: []Entity) Level {
|
||||
return Level{
|
||||
.world_x = x,
|
||||
.world_y = y,
|
||||
.width = width,
|
||||
.size = buf.len,
|
||||
.entity_count = entities.len,
|
||||
.tiles = buf,
|
||||
.entities = entities,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn write(level: Level, writer: anytype) !void {
|
||||
var tiles = level.tiles orelse return error.NullTiles;
|
||||
try writer.writeInt(u8, level.world_x, .Big);
|
||||
try writer.writeInt(u8, level.world_y, .Big);
|
||||
try writer.writeInt(u16, level.width, .Big);
|
||||
try writer.writeInt(u16, level.size, .Big);
|
||||
var entities = level.entities orelse return error.NullEntities;
|
||||
try writer.writeInt(u8, level.world_x, .Little);
|
||||
try writer.writeInt(u8, level.world_y, .Little);
|
||||
try writer.writeInt(u16, level.width, .Little);
|
||||
try writer.writeInt(u16, level.size, .Little);
|
||||
try writer.writeInt(u16, level.entity_count, .Little);
|
||||
|
||||
for (tiles) |tile| {
|
||||
try writer.writeByte(tile.toByte());
|
||||
}
|
||||
|
||||
for (entities) |entity| {
|
||||
try entity.write(writer);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read(reader: anytype) !Level {
|
||||
return Level{
|
||||
.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),
|
||||
.world_x = try reader.readInt(u8, .Little),
|
||||
.world_y = try reader.readInt(u8, .Little),
|
||||
.width = try reader.readInt(u16, .Little),
|
||||
.size = try reader.readInt(u16, .Little),
|
||||
.tiles = null,
|
||||
.entities = null,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -92,6 +105,25 @@ pub const Level = struct {
|
|||
buf[i] = TileData.fromByte(try reader.readByte());
|
||||
}
|
||||
}
|
||||
|
||||
pub fn readEntities(level: *Level, reader: anytype, buf: []Entity) !void {
|
||||
std.debug.assert(buf.len >= level.entity_count);
|
||||
level.entities = buf;
|
||||
var i: usize = 0;
|
||||
while (i < level.entity_count) : (i += 1) {
|
||||
buf[i] = Entity.read(reader);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn getSpawn(level: *Level) ?[2]i16 {
|
||||
std.debug.assert(level.entities != null);
|
||||
for (level.entities) |entity| {
|
||||
if (entity.kind == .Player) {
|
||||
return [2]i16{ entity.x, entity.y };
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
// AutoTile algorithm datatypes
|
||||
|
@ -192,3 +224,30 @@ pub const AutoTileset = struct {
|
|||
}
|
||||
}
|
||||
};
|
||||
|
||||
pub const EntityKind = enum(u8) {
|
||||
Player,
|
||||
Coin,
|
||||
WireNode,
|
||||
WireEndNode,
|
||||
Door,
|
||||
Trapdoor,
|
||||
};
|
||||
|
||||
pub const Entity = struct {
|
||||
kind: EntityKind,
|
||||
x: i16,
|
||||
y: i16,
|
||||
|
||||
pub fn write(entity: Entity, writer: anytype) !void {
|
||||
try writer.writeInt(u8, @enumToInt(entity.kind), .Little);
|
||||
try writer.writeInt(i16, entity.x, .Little);
|
||||
try writer.writeInt(i16, entity.y, .Little);
|
||||
}
|
||||
|
||||
pub fn read(entity: Entity, reader: anytype) !void {
|
||||
try reader.readInt(u8, @intToEnum(EntityKind, entity.kind), .Little);
|
||||
try reader.readInt(i16, entity.x, .Little);
|
||||
try reader.readInt(i16, entity.y, .Little);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -51,8 +51,10 @@ fn make(step: *std.build.Step) !void {
|
|||
defer data.deinit();
|
||||
const writer = data.writer();
|
||||
|
||||
const ldtk = try LDtk.parse(allocator, source);
|
||||
defer ldtk.deinit(allocator);
|
||||
var ldtk_parser = try LDtk.parse(allocator, source);
|
||||
defer ldtk_parser.deinit();
|
||||
|
||||
const ldtk = ldtk_parser.root;
|
||||
|
||||
if (ldtk.levels.len > 0) {
|
||||
const level0 = ldtk.levels[0];
|
||||
|
@ -60,23 +62,85 @@ fn make(step: *std.build.Step) !void {
|
|||
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 entity_array = std.ArrayList(world.Entity).init(allocator);
|
||||
defer entity_array.deinit();
|
||||
|
||||
var circuit_layer: ?LDtk.LayerInstance = null;
|
||||
var collision_layer: ?LDtk.LayerInstance = null;
|
||||
for (layers) |layer| {
|
||||
if (std.mem.eql(u8, layer.__identifier, "Entities")) {
|
||||
// Entities
|
||||
std.debug.assert(layer.__type == .Entities);
|
||||
|
||||
for (layer.entityInstances) |entity| {
|
||||
std.log.warn("{s}", .{entity.__identifier});
|
||||
var kind_opt: ?world.EntityKind = null;
|
||||
if (std.mem.eql(u8, entity.__identifier, "Player")) {
|
||||
kind_opt = .Player;
|
||||
} else if (std.mem.eql(u8, entity.__identifier, "Wire")) {
|
||||
kind_opt = .WireNode;
|
||||
} else if (std.mem.eql(u8, entity.__identifier, "Coin")) {
|
||||
kind_opt = .Coin;
|
||||
} else if (std.mem.eql(u8, entity.__identifier, "Door")) {
|
||||
kind_opt = .Door;
|
||||
} else if (std.mem.eql(u8, entity.__identifier, "Trapdoor")) {
|
||||
kind_opt = .Trapdoor;
|
||||
}
|
||||
|
||||
if (kind_opt) |kind| {
|
||||
if (kind != .WireNode) {
|
||||
const world_entity = world.Entity{
|
||||
.kind = kind,
|
||||
.x = @intCast(i16, entity.__grid[0]),
|
||||
.y = @intCast(i16, entity.__grid[1]),
|
||||
};
|
||||
try entity_array.append(world_entity);
|
||||
} else {
|
||||
const wire_begin = world.Entity{
|
||||
.kind = .WireNode,
|
||||
.x = @intCast(i16, entity.__grid[0]),
|
||||
.y = @intCast(i16, entity.__grid[1]),
|
||||
};
|
||||
try entity_array.append(wire_begin);
|
||||
|
||||
for (entity.fieldInstances) |field| {
|
||||
if (std.mem.eql(u8, field.__identifier, "Point")) {
|
||||
const end = field.__value.Array.items.len - 1;
|
||||
const endpoint = field.__value.Array.items[end];
|
||||
// const jstr = switch (endpoint) {
|
||||
// .Array => "Array",
|
||||
// .Object => "Object",
|
||||
// .Integer => "Integer",
|
||||
// else => "Other",
|
||||
// };
|
||||
// std.log.warn("{s}", .{jstr});
|
||||
// std.log.warn("{}", .{endpoint.Integer});
|
||||
// endpoint.dump();
|
||||
const x = endpoint.Object.get("cx").?;
|
||||
const y = endpoint.Object.get("cy").?;
|
||||
const wire_end = world.Entity{
|
||||
.kind = .WireEndNode,
|
||||
.x = @intCast(i16, x.Integer),
|
||||
.y = @intCast(i16, y.Integer),
|
||||
};
|
||||
try entity_array.append(wire_end);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (std.mem.eql(u8, layer.__identifier, "Circuit")) {
|
||||
// Circuit
|
||||
std.debug.assert(layer.__type == .IntGrid);
|
||||
|
||||
circuit_layer = layer;
|
||||
} else if (std.mem.eql(u8, layer.__identifier, "Collision")) {
|
||||
// Collision
|
||||
std.debug.assert(layer.__type == .IntGrid);
|
||||
|
||||
collision_layer = layer;
|
||||
} else {
|
||||
// Unknown
|
||||
std.log.warn("{s}: {}", .{ layer.__identifier, layer.__type });
|
||||
}
|
||||
}
|
||||
|
@ -100,13 +164,16 @@ fn make(step: *std.build.Step) !void {
|
|||
.world_y = world_y,
|
||||
.width = @intCast(u16, width),
|
||||
.size = @intCast(u16, size),
|
||||
.entity_count = @intCast(u16, entity_array.items.len),
|
||||
.tiles = null,
|
||||
.entities = entity_array.items,
|
||||
};
|
||||
level.tiles = try allocator.alloc(world.TileData, size);
|
||||
defer allocator.free(level.tiles.?);
|
||||
|
||||
const tiles = level.tiles.?;
|
||||
|
||||
// Add straight tile data
|
||||
for (collision.autoLayerTiles) |autotile| {
|
||||
const x = @divExact(autotile.px[0], collision.__gridSize);
|
||||
const y = @divExact(autotile.px[1], collision.__gridSize);
|
||||
|
@ -114,6 +181,7 @@ fn make(step: *std.build.Step) !void {
|
|||
tiles[i] = world.TileData{ .tile = @intCast(u7, autotile.t + 1) };
|
||||
}
|
||||
|
||||
// Add circuit tiles
|
||||
for (circuit.intGridCsv) |cir64, i| {
|
||||
const cir = @intCast(u4, cir64);
|
||||
const col = collision.intGridCsv[i];
|
||||
|
@ -125,6 +193,7 @@ fn make(step: *std.build.Step) !void {
|
|||
}
|
||||
}
|
||||
|
||||
// Save the level!
|
||||
try level.write(writer);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue