Add wires to database

master
Louis Pearson 2022-08-08 21:56:26 -06:00
parent 1404d6067e
commit af85ae5ae7
4 changed files with 233 additions and 74 deletions

View File

@ -149,7 +149,7 @@ var frame_fba_buf: [8192]u8 = undefined;
var frame_fba = std.heap.FixedBufferAllocator.init(&frame_fba_buf); var frame_fba = std.heap.FixedBufferAllocator.init(&frame_fba_buf);
var frame_alloc = frame_fba.allocator(); var frame_alloc = frame_fba.allocator();
var db_fba_buf: [1024]u8 = undefined; var db_fba_buf: [2046]u8 = undefined;
var db_fba = std.heap.FixedBufferAllocator.init(&db_fba_buf); var db_fba = std.heap.FixedBufferAllocator.init(&db_fba_buf);
var db_alloc = db_fba.allocator(); var db_alloc = db_fba.allocator();
@ -226,9 +226,11 @@ fn loadLevel(lvl: usize) !void {
{ {
_ = try wires.resize(0); _ = try wires.resize(0);
var a: usize = 0; var a: usize = 0;
while (db.getWire(level, a)) |wire| : (a += 1) { while (db.getWire(level, a)) |wireSlice| : (a += 1) {
const wire = try world.Wire.getEnds(wireSlice);
const coord0 = wire[0].coord.subC(levelc); const coord0 = wire[0].coord.subC(levelc);
const coord1 = wire[1].coord.subC(levelc); const coord1 = wire[1].coord.subC(levelc);
w4.tracef("---- Wire (%d, %d), (%d, %d)", coord0.val[0], coord0.val[1], coord1.val[0], coord1.val[1]);
const p1 = util.vec2ToVec2f(coord0.toVec2() * tile_size + Vec2{ 4, 4 }); const p1 = util.vec2ToVec2f(coord0.toVec2() * tile_size + Vec2{ 4, 4 });
const p2 = util.vec2ToVec2f(coord1.toVec2() * tile_size + Vec2{ 4, 4 }); const p2 = util.vec2ToVec2f(coord1.toVec2() * tile_size + Vec2{ 4, 4 });
@ -243,8 +245,8 @@ fn loadLevel(lvl: usize) !void {
w.begin().pos = p1; w.begin().pos = p1;
w.end().pos = p2; w.end().pos = p2;
w.begin().pinned = wire[0].kind == world.EntityKind.WireAnchor; w.begin().pinned = wire[0].anchored;
w.end().pinned = wire[1].kind == world.EntityKind.WireEndAnchor; w.end().pinned = wire[1].anchored;
w.straighten(); w.straighten();
} }
@ -530,6 +532,7 @@ pub fn update(time: usize) !State {
return .Game; return .Game;
} }
/// Holds data related to selecting/interacting with the world
const Interaction = struct { const Interaction = struct {
pos: Vec2, pos: Vec2,
details: union(enum) { details: union(enum) {

View File

@ -38,6 +38,7 @@ export fn update() void {
error.NoLevelUp => showErr(@errorName(e)), error.NoLevelUp => showErr(@errorName(e)),
error.NoLevelLeft => showErr(@errorName(e)), error.NoLevelLeft => showErr(@errorName(e)),
error.NoLevelRight => showErr(@errorName(e)), error.NoLevelRight => showErr(@errorName(e)),
error.MissingEnds => showErr(@errorName(e)),
}, },
}; };
if (state != newState) { if (state != newState) {
@ -52,6 +53,7 @@ export fn update() void {
error.NullTiles => showErr(@errorName(e)), error.NullTiles => showErr(@errorName(e)),
error.SpawnOutOfBounds => showErr(@errorName(e)), error.SpawnOutOfBounds => showErr(@errorName(e)),
error.InvalidLevel => showErr(@errorName(e)), error.InvalidLevel => showErr(@errorName(e)),
error.MissingEnds => showErr(@errorName(e)),
}, },
} }
} }

View File

@ -190,6 +190,10 @@ pub const Coordinate = struct {
return .{ .val = .{ coord.val[0] - other.val[0], coord.val[1] - other.val[1] } }; return .{ .val = .{ coord.val[0] - other.val[0], coord.val[1] - other.val[1] } };
} }
pub fn addOffset(coord: Coordinate, val: [2]i4) Coordinate {
return .{ .val = .{ coord.val[0] + val[0], coord.val[1] + val[1] } };
}
pub fn eq(coord: Coordinate, other: Coordinate) bool { pub fn eq(coord: Coordinate, other: Coordinate) bool {
return coord.val[0] == other.val[0] and coord.val[1] == other.val[1]; return coord.val[0] == other.val[0] and coord.val[1] == other.val[1];
} }
@ -209,6 +213,10 @@ pub const Coordinate = struct {
return .{ coord.val[0], coord.val[1] }; return .{ coord.val[0], coord.val[1] };
} }
pub fn toOffset(coord: Coordinate) [2]i4 {
return .{ @intCast(i4, coord.val[0]), @intCast(i4, coord.val[1]) };
}
pub fn fromWorld(x: i8, y: i8) Coordinate { pub fn fromWorld(x: i8, y: i8) Coordinate {
return .{ .val = .{ return .{ .val = .{
@intCast(i16, x) * 20, @intCast(i16, x) * 20,
@ -429,10 +437,6 @@ pub const AutoTileset = struct {
pub const EntityKind = enum(u8) { pub const EntityKind = enum(u8) {
Player, Player,
Coin, Coin,
WireNode,
WireAnchor,
WireEndNode,
WireEndAnchor,
Door, Door,
Trapdoor, Trapdoor,
Collected, Collected,
@ -461,12 +465,109 @@ pub const Entity = struct {
} }
}; };
// Data format: const WireKind = enum {
// | level count | node count | Begin,
// | level headers... | BeginPinned,
// | node data... | Point,
// | level data... | PointPinned,
End,
};
/// A wire is stored as a coordinate and at least one point relative to it,
/// and then an end byte
pub const Wire = union(enum) {
Begin: Coordinate,
BeginPinned: Coordinate,
/// Relative to the last point
Point: [2]i4,
/// Relative to the last point
PointPinned: [2]i4,
End,
const EndData = struct { coord: Coordinate, anchored: bool };
pub fn getEnds(wires: []Wire) ![2]EndData {
std.debug.assert(wires[0] == .Begin or wires[0] == .BeginPinned);
var ends: [2]EndData = undefined;
const w4 = @import("wasm4.zig");
for (wires) |wire| {
switch (wire) {
.Begin => |coord| {
ends[0] = .{ .coord = coord, .anchored = false };
ends[1] = ends[0];
w4.tracef("[getEnds] Begin (%d, %d)", coord.val[0], coord.val[1]);
},
.BeginPinned => |coord| {
ends[0] = .{ .coord = coord, .anchored = true };
ends[1] = ends[0];
w4.tracef("[getEnds] BeginPinned (%d, %d)", coord.val[0], coord.val[1]);
},
.Point => |offset| {
ends[1] = .{ .coord = ends[1].coord.addOffset(offset), .anchored = false };
const o1 = @intCast(i32, offset[0]);
const o2 = @intCast(i32, offset[1]);
w4.tracef("[getEnds] Point (%d, %d)", o1, o2);
},
.PointPinned => |offset| {
ends[1] = .{ .coord = ends[1].coord.addOffset(offset), .anchored = true };
const o1 = @intCast(i32, offset[0]);
const o2 = @intCast(i32, offset[1]);
w4.tracef("[getEnds] Point Pinned (%d, %d)", o1, o2);
},
.End => {
return ends;
},
}
}
return error.MissingEnds;
}
pub fn write(wire: Wire, writer: anytype) !void {
try writer.writeByte(@enumToInt(wire));
switch (wire) {
.Begin => |coord| {
try coord.write(writer);
},
.BeginPinned => |coord| {
try coord.write(writer);
},
.Point => |point| {
const byte = @bitCast(u8, @intCast(i8, point[0])) | @bitCast(u8, @intCast(i8, point[1])) << 4;
try writer.writeByte(byte);
},
.PointPinned => |point| {
const byte = @bitCast(u8, @intCast(i8, point[0])) | @bitCast(u8, @intCast(i8, point[1])) << 4;
try writer.writeByte(byte);
},
.End => {},
}
}
pub fn read(reader: anytype) !Wire {
const kind = @intToEnum(WireKind, try reader.readByte());
switch (kind) {
.Begin => return Wire{ .Begin = try Coord.read(reader) },
.BeginPinned => return Wire{ .BeginPinned = try Coord.read(reader) },
.Point => {
const byte = try reader.readByte();
return Wire{ .Point = .{
@bitCast(i4, @truncate(u4, 0b0000_1111 & byte)),
@bitCast(i4, @truncate(u4, (0b1111_0000 & byte) >> 4)),
} };
},
.PointPinned => {
const byte = try reader.readByte();
return Wire{ .PointPinned = .{
@bitCast(i4, @truncate(u4, 0b0000_1111 & byte)),
@bitCast(i4, @truncate(u4, (0b1111_0000 & byte) >> 4)),
} };
},
.End => return Wire.End,
}
}
};
/// Used to look up level data
pub const LevelHeader = struct { pub const LevelHeader = struct {
x: i8, x: i8,
y: i8, y: i8,
@ -491,6 +592,7 @@ pub fn write(
writer: anytype, writer: anytype,
level_headers: []LevelHeader, level_headers: []LevelHeader,
entities: []Entity, entities: []Entity,
wires: []Wire,
circuit_nodes: []CircuitNode, circuit_nodes: []CircuitNode,
levels: []Level, levels: []Level,
) !void { ) !void {
@ -498,6 +600,8 @@ pub fn write(
try writer.writeInt(u16, @intCast(u16, level_headers.len), .Little); try writer.writeInt(u16, @intCast(u16, level_headers.len), .Little);
// Write number of entities // Write number of entities
try writer.writeInt(u16, @intCast(u16, entities.len), .Little); try writer.writeInt(u16, @intCast(u16, entities.len), .Little);
// Write number of entities
try writer.writeInt(u16, @intCast(u16, wires.len), .Little);
// Write number of circuit nodes // Write number of circuit nodes
try writer.writeInt(u16, @intCast(u16, circuit_nodes.len), .Little); try writer.writeInt(u16, @intCast(u16, circuit_nodes.len), .Little);
@ -511,6 +615,11 @@ pub fn write(
try entity.write(writer); try entity.write(writer);
} }
// Write wire data
for (wires) |wire| {
try wire.write(writer);
}
// Write node data // Write node data
for (circuit_nodes) |node| { for (circuit_nodes) |node| {
try node.write(writer); try node.write(writer);
@ -527,6 +636,8 @@ pub const Database = struct {
cursor: Cursor, cursor: Cursor,
level_info: []LevelHeader, level_info: []LevelHeader,
entities: []Entity, entities: []Entity,
wires: []Wire,
wire_count: usize,
circuit_info: []CircuitNode, circuit_info: []CircuitNode,
level_data_begin: usize, level_data_begin: usize,
@ -544,6 +655,8 @@ pub const Database = struct {
const level_count = try reader.readInt(u16, .Little); const level_count = try reader.readInt(u16, .Little);
// read number of entities // read number of entities
const entity_count = try reader.readInt(u16, .Little); const entity_count = try reader.readInt(u16, .Little);
// read number of wires
const wire_count = try reader.readInt(u16, .Little);
// read number of nodes // read number of nodes
const node_count = try reader.readInt(u16, .Little); const node_count = try reader.readInt(u16, .Little);
@ -561,6 +674,16 @@ pub const Database = struct {
entities[i] = try Entity.read(reader); entities[i] = try Entity.read(reader);
} }
// read wires
// Allocate a fixed amount of space since wires are likely to be shuffled around
var wires = try alloc.alloc(Wire, 255);
{
var i: usize = 0;
while (i < wire_count) : (i += 1) {
wires[i] = try Wire.read(reader);
}
}
// read circuits // read circuits
var circuit_nodes = try alloc.alloc(CircuitNode, node_count); var circuit_nodes = try alloc.alloc(CircuitNode, node_count);
@ -575,6 +698,8 @@ pub const Database = struct {
.cursor = cursor, .cursor = cursor,
.level_info = level_headers, .level_info = level_headers,
.entities = entities, .entities = entities,
.wires = wires,
.wire_count = wire_count,
.circuit_info = circuit_nodes, .circuit_info = circuit_nodes,
.level_data_begin = level_data_begin, .level_data_begin = level_data_begin,
}; };
@ -732,20 +857,33 @@ pub const Database = struct {
db.entities[coin].kind = .Collected; db.entities[coin].kind = .Collected;
} }
pub fn getWire(database: *Database, level: Level, num: usize) ?[2]Entity { pub fn getWire(database: *Database, level: Level, num: usize) ?[]Wire {
const nw = Coord.fromWorld(level.world_x, level.world_y); const nw = Coord.fromWorld(level.world_x, level.world_y);
const se = nw.add(.{ 20, 20 }); const se = nw.add(.{ 20, 20 });
var node_begin: ?Entity = null; var node_begin: ?usize = null;
var wire_count: usize = 0; var wire_count: usize = 0;
for (database.entities) |entity| { var i: usize = 0;
if (!entity.coord.within(nw, se)) continue; while (i < database.wire_count) : (i += 1) {
if (entity.kind == .WireNode or entity.kind == .WireAnchor) { const wire = database.wires[i];
node_begin = entity; switch (wire) {
} else if (entity.kind == .WireEndNode or entity.kind == .WireEndAnchor) { .Begin => |coord| {
if (node_begin) |begin| { if (!coord.within(nw, se)) continue;
if (wire_count == num) return [2]Entity{ begin, entity }; node_begin = i;
} },
wire_count += 1; .BeginPinned => |coord| {
if (!coord.within(nw, se)) continue;
node_begin = i;
},
.Point, .PointPinned => continue,
.End => {
if (node_begin) |node| {
if (wire_count == num) {
return database.wires[node..i + 1];
}
wire_count += 1;
node_begin = null;
}
},
} }
} }
return null; return null;

View File

@ -58,6 +58,9 @@ fn make(step: *std.build.Step) !void {
var entity_array = std.ArrayList(world.Entity).init(allocator); var entity_array = std.ArrayList(world.Entity).init(allocator);
defer entity_array.deinit(); defer entity_array.deinit();
var wires = std.ArrayList(world.Wire).init(allocator);
defer wires.deinit();
for (ldtk.levels) |level| { for (ldtk.levels) |level| {
std.log.warn("Level: {}", .{levels.items.len}); std.log.warn("Level: {}", .{levels.items.len});
const parsed_level = try parseLevel(.{ const parsed_level = try parseLevel(.{
@ -65,6 +68,7 @@ fn make(step: *std.build.Step) !void {
.ldtk = ldtk, .ldtk = ldtk,
.level = level, .level = level,
.entity_array = &entity_array, .entity_array = &entity_array,
.wires = &wires,
}); });
try levels.append(parsed_level); try levels.append(parsed_level);
@ -80,6 +84,10 @@ fn make(step: *std.build.Step) !void {
std.log.warn("{:0>2}: {}", .{ i, node }); std.log.warn("{:0>2}: {}", .{ i, node });
} }
for (wires.items) |node, i| {
std.log.warn("Wire {:0>2}: {any}", .{ i, node });
}
// Calculate the offset of each level and store it in the headers. // Calculate the offset of each level and store it in the headers.
// Offset is relative to the beginning of level.data // Offset is relative to the beginning of level.data
var level_headers = std.ArrayList(world.LevelHeader).init(allocator); var level_headers = std.ArrayList(world.LevelHeader).init(allocator);
@ -109,7 +117,14 @@ fn make(step: *std.build.Step) !void {
defer data.deinit(); defer data.deinit();
const writer = data.writer(); const writer = data.writer();
try world.write(writer, level_headers.items, entity_array.items, circuit.items, levels.items); try world.write(
writer,
level_headers.items,
entity_array.items,
wires.items,
circuit.items,
levels.items,
);
// Open output file and write data into it // Open output file and write data into it
cwd.makePath(this.builder.getInstallPath(.lib, "")) catch |e| switch (e) { cwd.makePath(this.builder.getInstallPath(.lib, "")) catch |e| switch (e) {
@ -127,11 +142,13 @@ fn parseLevel(opt: struct {
ldtk: LDtk.Root, ldtk: LDtk.Root,
level: LDtk.Level, level: LDtk.Level,
entity_array: *std.ArrayList(world.Entity), entity_array: *std.ArrayList(world.Entity),
wires: *std.ArrayList(world.Wire),
}) !world.Level { }) !world.Level {
const ldtk = opt.ldtk; const ldtk = opt.ldtk;
const level = opt.level; const level = opt.level;
const entity_array = opt.entity_array; const entity_array = opt.entity_array;
const allocator = opt.allocator; const allocator = opt.allocator;
const wires = opt.wires;
const layers = level.layerInstances orelse return error.NoLayers; const layers = level.layerInstances orelse return error.NoLayers;
@ -147,11 +164,12 @@ fn parseLevel(opt: struct {
std.debug.assert(layer.__type == .Entities); std.debug.assert(layer.__type == .Entities);
for (layer.entityInstances) |entity| { for (layer.entityInstances) |entity| {
var is_wire = false;
var kind_opt: ?world.EntityKind = null; var kind_opt: ?world.EntityKind = null;
if (std.mem.eql(u8, entity.__identifier, "Player")) { if (std.mem.eql(u8, entity.__identifier, "Player")) {
kind_opt = .Player; kind_opt = .Player;
} else if (std.mem.eql(u8, entity.__identifier, "Wire")) { } else if (std.mem.eql(u8, entity.__identifier, "Wire")) {
kind_opt = .WireNode; is_wire = true;
} else if (std.mem.eql(u8, entity.__identifier, "Coin")) { } else if (std.mem.eql(u8, entity.__identifier, "Coin")) {
kind_opt = .Coin; kind_opt = .Coin;
} else if (std.mem.eql(u8, entity.__identifier, "Door")) { } else if (std.mem.eql(u8, entity.__identifier, "Door")) {
@ -160,59 +178,57 @@ fn parseLevel(opt: struct {
kind_opt = .Trapdoor; kind_opt = .Trapdoor;
} }
const levelc = world.Coordinate.fromWorld(world_x, world_y);
// Parsing code for wire entities. They're a little more complex // Parsing code for wire entities. They're a little more complex
// than the rest // than the rest
if (kind_opt) |kind| { if (kind_opt) |kind| {
const levelc = world.Coordinate.fromWorld(world_x, world_y); const entc = world.Coordinate.init(.{
if (kind != .WireNode) { @intCast(i16, entity.__grid[0]),
const entc = world.Coordinate.init(.{ @intCast(i16, entity.__grid[1]),
@intCast(i16, entity.__grid[0]), });
@intCast(i16, entity.__grid[1]), const world_entity = world.Entity{ .kind = kind, .coord = levelc.addC(entc) };
}); try entity_array.append(world_entity);
const world_entity = world.Entity{ }
.kind = kind, if (is_wire) {
.coord = levelc.addC(entc) var anchor1 = false;
}; var anchor2 = false;
try entity_array.append(world_entity); const p1_c = world.Coordinate.init(.{
} else { @intCast(i16, entity.__grid[0]),
var anchor1 = false; @intCast(i16, entity.__grid[1]),
var anchor2 = false; });
const p1_c = world.Coordinate.init(.{ var p2_c = world.Coordinate.init(.{
@intCast(i16, entity.__grid[0]), @intCast(i16, entity.__grid[0]),
@intCast(i16, entity.__grid[1]), @intCast(i16, entity.__grid[1]),
}); });
var p2_c = world.Coordinate.init(.{ for (entity.fieldInstances) |field| {
@intCast(i16, entity.__grid[0]), if (std.mem.eql(u8, field.__identifier, "Anchor")) {
@intCast(i16, entity.__grid[1]), const anchors = field.__value.Array.items;
}); anchor1 = anchors[0].Bool;
for (entity.fieldInstances) |field| { anchor2 = anchors[1].Bool;
if (std.mem.eql(u8, field.__identifier, "Anchor")) { } else if (std.mem.eql(u8, field.__identifier, "Point")) {
const anchors = field.__value.Array.items; const end = field.__value.Array.items.len - 1;
anchor1 = anchors[0].Bool; const endpoint = field.__value.Array.items[end];
anchor2 = anchors[1].Bool; const x = endpoint.Object.get("cx").?;
} else if (std.mem.eql(u8, field.__identifier, "Point")) { const y = endpoint.Object.get("cy").?;
const end = field.__value.Array.items.len - 1; p2_c.val = .{
const endpoint = field.__value.Array.items[end]; @intCast(i16, x.Integer),
const x = endpoint.Object.get("cx").?; @intCast(i16, y.Integer),
const y = endpoint.Object.get("cy").?; };
p2_c.val = .{
@intCast(i16, x.Integer),
@intCast(i16, y.Integer),
};
}
} }
const wire_begin = world.Entity{
.kind = if (anchor1) .WireAnchor else .WireNode,
.coord = p1_c.addC(levelc),
};
try entity_array.append(wire_begin);
const wire_end = world.Entity{
.kind = if (anchor2) .WireEndAnchor else .WireEndNode,
.coord = p2_c.addC(levelc),
};
try entity_array.append(wire_end);
} }
if (anchor1) {
try wires.append(.{ .BeginPinned = p1_c.addC(levelc) });
} else {
try wires.append(.{ .Begin = p1_c.addC(levelc) });
}
if (anchor2) {
try wires.append(.{ .PointPinned = p2_c.subC(p1_c).toOffset() });
} else {
try wires.append(.{ .Point = p2_c.subC(p1_c).toOffset() });
}
try wires.append(.End);
} }
} }