Make circuit rely on database

master
Louis Pearson 2022-08-09 20:49:43 -06:00
parent 3036ccd1e8
commit d3450e4323
6 changed files with 207 additions and 173 deletions

View File

@ -4246,9 +4246,9 @@
"defUid": 93, "defUid": 93,
"px": [84,124], "px": [84,124],
"fieldInstances": [ "fieldInstances": [
{ "__identifier": "Point", "__value": [{ "cx": 15, "cy": 15 }], "__type": "Array<Point>", "__tile": null, "defUid": 94, "realEditorValues": [{ { "__identifier": "Point", "__value": [{ "cx": 14, "cy": 15 }], "__type": "Array<Point>", "__tile": null, "defUid": 94, "realEditorValues": [{
"id": "V_String", "id": "V_String",
"params": ["15,15"] "params": ["14,15"]
}] }, }] },
{ "__identifier": "Anchor", "__value": [ true, true ], "__type": "Array<Bool>", "__tile": null, "defUid": 98, "realEditorValues": [ { { "__identifier": "Anchor", "__value": [ true, true ], "__type": "Array<Bool>", "__tile": null, "defUid": 98, "realEditorValues": [ {
"id": "V_Bool", "id": "V_Bool",
@ -4330,12 +4330,13 @@
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,1,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,1,0,
0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,1,0,0,1,0,0,1,1,1,1,0,0,0,1,0,0,0, 0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,1,0,0,1,0,0,1,1,1,1,0,0,0,1,0,0,0,
0,0,0,1,0,0,1,0,0,1,0,0,1,0,0,0,1,0,0,0,0,0,0,1,0,0,1,1,1,7,10,0,1,0,0, 0,0,0,1,0,0,1,0,0,1,0,0,1,0,9,0,1,0,0,0,0,0,0,1,0,0,1,1,1,7,10,0,1,0,2,
2,1,0,0,0,0,0,0,1,0,0,4,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,1,0,0,1,0,1,1, 0,1,0,0,0,0,0,0,1,0,0,4,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,1,0,0,1,0,1,1,
1,1,1,0,0,0,1,0,0,0,0,0,0,8,0,0,9,0,8,0,0,0,0,0,0,0,8,0,0,0,0,0,0,0,0, 1,1,1,0,0,0,1,0,0,0,0,0,0,8,0,0,9,0,8,0,0,0,0,0,0,0,8,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
], ],
"autoLayerTiles": [ "autoLayerTiles": [
{ "px": [112,112], "src": [64,48], "f": 0, "t": 104, "d": [106,294] },
{ "px": [48,144], "src": [8,48], "f": 0, "t": 97, "d": [104,366] }, { "px": [48,144], "src": [8,48], "f": 0, "t": 97, "d": [104,366] },
{ "px": [24,88], "src": [96,48], "f": 0, "t": 108, "d": [35,223] }, { "px": [24,88], "src": [96,48], "f": 0, "t": 108, "d": [35,223] },
{ "px": [72,104], "src": [96,48], "f": 0, "t": 108, "d": [35,269] }, { "px": [72,104], "src": [96,48], "f": 0, "t": 108, "d": [35,269] },
@ -4345,7 +4346,6 @@
{ "px": [96,136], "src": [24,48], "f": 0, "t": 99, "d": [37,352] }, { "px": [96,136], "src": [24,48], "f": 0, "t": 99, "d": [37,352] },
{ "px": [48,120], "src": [104,48], "f": 0, "t": 109, "d": [40,306] }, { "px": [48,120], "src": [104,48], "f": 0, "t": 109, "d": [40,306] },
{ "px": [48,88], "src": [112,48], "f": 0, "t": 110, "d": [41,226] }, { "px": [48,88], "src": [112,48], "f": 0, "t": 110, "d": [41,226] },
{ "px": [128,120], "src": [88,48], "f": 0, "t": 107, "d": [43,316] },
{ "px": [32,88], "src": [48,48], "f": 0, "t": 102, "d": [44,224] }, { "px": [32,88], "src": [48,48], "f": 0, "t": 102, "d": [44,224] },
{ "px": [40,88], "src": [48,48], "f": 0, "t": 102, "d": [44,225] }, { "px": [40,88], "src": [48,48], "f": 0, "t": 102, "d": [44,225] },
{ "px": [56,88], "src": [48,48], "f": 0, "t": 102, "d": [44,227] }, { "px": [56,88], "src": [48,48], "f": 0, "t": 102, "d": [44,227] },
@ -4377,6 +4377,7 @@
{ "px": [128,112], "src": [72,48], "f": 0, "t": 105, "d": [45,296] }, { "px": [128,112], "src": [72,48], "f": 0, "t": 105, "d": [45,296] },
{ "px": [24,120], "src": [72,48], "f": 0, "t": 105, "d": [45,303] }, { "px": [24,120], "src": [72,48], "f": 0, "t": 105, "d": [45,303] },
{ "px": [96,120], "src": [72,48], "f": 0, "t": 105, "d": [45,312] }, { "px": [96,120], "src": [72,48], "f": 0, "t": 105, "d": [45,312] },
{ "px": [128,120], "src": [72,48], "f": 0, "t": 105, "d": [45,316] },
{ "px": [24,128], "src": [72,48], "f": 0, "t": 105, "d": [45,323] }, { "px": [24,128], "src": [72,48], "f": 0, "t": 105, "d": [45,323] },
{ "px": [96,128], "src": [72,48], "f": 0, "t": 105, "d": [45,332] }, { "px": [96,128], "src": [72,48], "f": 0, "t": 105, "d": [45,332] },
{ "px": [128,128], "src": [72,48], "f": 0, "t": 105, "d": [45,336] }, { "px": [128,128], "src": [72,48], "f": 0, "t": 105, "d": [45,336] },
@ -4384,7 +4385,7 @@
{ "px": [48,136], "src": [72,48], "f": 0, "t": 105, "d": [45,346] }, { "px": [48,136], "src": [72,48], "f": 0, "t": 105, "d": [45,346] },
{ "px": [128,136], "src": [72,48], "f": 0, "t": 105, "d": [45,356] }, { "px": [128,136], "src": [72,48], "f": 0, "t": 105, "d": [45,356] },
{ "px": [80,120], "src": [8,8], "f": 0, "t": 17, "d": [130,310] }, { "px": [80,120], "src": [8,8], "f": 0, "t": 17, "d": [130,310] },
{ "px": [120,120], "src": [16,8], "f": 0, "t": 18, "d": [59,315] }, { "px": [112,120], "src": [0,8], "f": 0, "t": 16, "d": [57,314] },
{ "px": [48,128], "src": [104,8], "f": 0, "t": 29, "d": [79,326] }, { "px": [48,128], "src": [104,8], "f": 0, "t": 29, "d": [79,326] },
{ "px": [72,120], "src": [48,8], "f": 0, "t": 22, "d": [83,309] } { "px": [72,120], "src": [48,8], "f": 0, "t": 22, "d": [83,309] }
], ],

View File

@ -139,15 +139,15 @@ fn get_plugs(tile: u8) Plugs {
}; };
} }
pub fn get_cell(this: @This(), cell: Cell) ?u8 { pub fn getCoord(this: @This(), coord: world.Coordinate) ?u8 {
const i = this.indexOf(cell) orelse return null; const i = this.indexOf(coord.toVec2()) orelse return null;
return if (this.map[i] != 0) this.map[i] else null; return if (this.map[i] != 0) this.map[i] else null;
} }
pub fn set_cell(this: *@This(), cell: Cell, tile: u8) void { pub fn setCoord(this: @This(), coord: world.Coordinate, tile: u8) void {
const i = this.indexOf(cell) orelse return; const i = this.indexOf(coord.toVec2()) orelse return;
this.map[i] = tile; this.map[i] = tile;
this.levels[i] = 0; // this.levels[i] = 255;
} }
const MAXCELLS = 400; const MAXCELLS = 400;
@ -156,37 +156,37 @@ const MAXSOURCES = 10;
const MAXDOORS = 40; const MAXDOORS = 40;
const MAXLOGIC = 40; const MAXLOGIC = 40;
pub const CellData = struct { level: u8 = 0, tile: u8 }; pub const NodeCoord = struct { coord: world.Coordinate, node_id: world.NodeID };
pub const Source = NodeCoord;
pub const BridgeState = struct { cells: [2]Cell, id: usize, enabled: bool }; pub const BridgeState = struct { coords: [2]world.Coordinate, id: usize, enabled: bool };
pub const DoorState = struct { cell: Cell, enabled: bool }; pub const DoorState = struct { coord: world.Coordinate, enabled: bool };
/// Tile id of the tiles /// Tile id of the tiles
map: []u8, map: []u8,
/// Logic levels of the tiles /// CircuitNode ID for each tile
levels: []u8, nodes: []world.NodeID,
map_size: Vec2, map_size: Vec2,
bridges: util.Buffer(BridgeState), bridges: util.Buffer(BridgeState),
sources: util.Buffer(Cell), sources: util.Buffer(Source),
doors: util.Buffer(DoorState), doors: util.Buffer(DoorState),
pub const Options = struct { pub const Options = struct {
map: []u8, map: []u8,
levels: []u8, nodes: []u8,
map_size: Vec2, map_size: Vec2,
bridges: []BridgeState, bridges: []BridgeState,
sources: []Cell, sources: []Source,
doors: []DoorState, doors: []DoorState,
}; };
pub fn init(opt: Options) @This() { pub fn init(opt: Options) @This() {
std.debug.assert(opt.map.len == opt.levels.len); std.debug.assert(opt.map.len == opt.nodes.len);
var this = @This(){ var this = @This(){
.map = opt.map, .map = opt.map,
.levels = opt.levels, .nodes = opt.nodes,
.map_size = opt.map_size, .map_size = opt.map_size,
.bridges = util.Buffer(BridgeState).init(opt.bridges), .bridges = util.Buffer(BridgeState).init(opt.bridges),
.sources = util.Buffer(Cell).init(opt.sources), .sources = util.Buffer(Source).init(opt.sources),
.doors = util.Buffer(DoorState).init(opt.doors), .doors = util.Buffer(DoorState).init(opt.doors),
}; };
return this; return this;
@ -197,31 +197,40 @@ pub fn indexOf(this: @This(), cell: Cell) ?usize {
return @intCast(usize, @mod(cell[0], this.map_size[0]) + (cell[1] * this.map_size[1])); return @intCast(usize, @mod(cell[0], this.map_size[0]) + (cell[1] * this.map_size[1]));
} }
pub fn enable(this: *@This(), cell: Cell) void { pub fn bridge(this: *@This(), coords: [2]world.Coordinate, bridgeID: usize) void {
const i = this.indexOf(cell) orelse return; if (this.indexOf(coords[0].toVec2())) |_| {
this.levels[i] += 1; if (this.indexOf(coords[1].toVec2())) |_| {
} this.bridges.append(.{ .coords = coords, .id = bridgeID, .enabled = false });
pub fn bridge(this: *@This(), cells: [2]Cell, bridgeID: usize) void {
if (this.indexOf(cells[0])) |_| {
if (this.indexOf(cells[1])) |_| {
this.bridges.append(.{ .cells = cells, .id = bridgeID, .enabled = false });
} }
} }
} }
pub fn addSource(this: *@This(), cell: Cell) void { pub fn addSource(this: *@This(), source: Source) void {
if (this.indexOf(cell)) |_| { w4.tracef("%d, %d", source.coord.val[0], source.coord.val[1]);
this.sources.append(cell); if (this.indexOf(source.coord.toVec2())) |_| {
this.sources.append(source);
} }
} }
pub fn addDoor(this: *@This(), cell: Cell) !void { pub fn addDoor(this: *@This(), coord: world.Coordinate) !void {
if (this.indexOf(cell)) |_| { if (this.indexOf(coord.toVec2())) |_| {
this.doors.append(.{ .cell = cell, .enabled = false }); this.doors.append(.{ .coord = coord, .enabled = false });
} }
} }
pub fn enabledBridge(this: @This(), id: usize) ?usize {
var count: usize = 0;
for (this.bridges.items) |b| {
if (b.enabled) {
if (count == id) {
return b.id;
}
count += 1;
}
}
return null;
}
pub fn enabledBridges(this: @This(), alloc: std.mem.Allocator) !util.Buffer(usize) { pub fn enabledBridges(this: @This(), alloc: std.mem.Allocator) !util.Buffer(usize) {
var items = try alloc.alloc(usize, this.bridges.len); var items = try alloc.alloc(usize, this.bridges.len);
var buffer = util.Buffer(usize).init(items); var buffer = util.Buffer(usize).init(items);
@ -243,28 +252,28 @@ pub fn enabledDoors(this: @This(), alloc: std.mem.Allocator) !util.Buffer(Cell)
return buffer; return buffer;
} }
pub fn isEnabled(this: @This(), cell: Cell) bool { pub fn getNodeID(this: @This(), coord: world.Coordinate) ?world.NodeID {
const i = this.indexOf(cell) orelse return false; const i = this.indexOf(coord.toVec2()) orelse return null;
return this.levels[i] >= 1; if (this.nodes[i] == std.math.maxInt(world.NodeID)) return null;
return this.nodes[i];
} }
pub fn switchOn(this: *@This(), cell: Cell) void { pub fn switchOn(this: *@This(), coord: world.Coordinate) void {
if (this.get_cell(cell)) |tile| { if (this.getCoord(coord)) |tile| {
if (T.is_switch(tile)) { if (T.is_switch(tile)) {
if (switchIsOn(tile)) return; if (switchIsOn(tile)) return;
const toggled = toggle_switch(tile); const toggled = toggle_switch(tile);
this.set_cell(cell, toggled); this.setCoord(coord, toggled);
return; return;
} }
} }
} }
pub fn toggle(this: *@This(), c: Cell) ?u8 { pub fn toggle(this: *@This(), coord: world.Coordinate) ?u8 {
const cell = c; if (this.getCoord(coord)) |tile| {
if (this.get_cell(cell)) |tile| {
if (T.is_switch(tile)) { if (T.is_switch(tile)) {
const toggled = toggle_switch(tile); const toggled = toggle_switch(tile);
this.set_cell(cell, toggled); this.setCoord(coord, toggled);
return toggled; return toggled;
} }
} }
@ -280,7 +289,7 @@ pub fn clearMap(this: *@This()) void {
} }
pub fn clear(this: *@This()) void { pub fn clear(this: *@This()) void {
std.mem.set(u8, this.levels, 0); std.mem.set(u8, this.nodes, std.math.maxInt(world.NodeID));
for (this.doors.items) |*door| { for (this.doors.items) |*door| {
door.enabled = false; door.enabled = false;
} }
@ -294,70 +303,87 @@ pub fn reset(this: *@This()) void {
} }
const w4 = @import("wasm4.zig"); const w4 = @import("wasm4.zig");
const Queue = util.Queue(Cell);
// Returns number of cells filled // Returns number of cells filled
pub fn fill(this: *@This(), alloc: std.mem.Allocator) !usize { pub fn fill(this: *@This(), alloc: std.mem.Allocator, db: world.Database, level: world.Level) !usize {
var count: usize = 0; var count: usize = 0;
w4.tracef("[fill] begin");
var q_buf = try alloc.alloc(Cell, MAXCELLS); const Queue = util.Queue(NodeCoord);
var q_buf = try alloc.alloc(NodeCoord, MAXCELLS);
var q = Queue.init(q_buf); var q = Queue.init(q_buf);
for (this.sources.items) |source| { for (this.sources.items) |source| {
w4.tracef("[fill] inserting source (%d, %d)", source.coord.val[0], source.coord.val[1]);
try q.insert(source); try q.insert(source);
} }
// if (this.sources.items.len == 0) {
// w4.tracef("[fill] no sources %d", this.sources.items.len);
// }
while (q.remove()) |cell| { while (q.remove()) |node| {
const tile = this.get_cell(cell) orelse { const tile = this.getCoord(node.coord) orelse continue;
for (this.doors.items) |*d| { const index = this.indexOf(node.coord.toVec2()) orelse continue;
if (@reduce(.And, d.cell == cell)) { const hasVisited = this.nodes[index] != std.math.maxInt(world.NodeID);
d.enabled = true;
} w4.tracef("[fill] %d, %d, %d", tile, index, hasVisited);
} if (hasVisited) continue;
this.nodes[index] = node.node_id;
count += 1;
if (get_logic(tile)) |_| {
// w4.tracef("[fill] logic");
const new_id = db.getLevelNodeID(level, node.coord) orelse {
w4.tracef("[fill] missing logic");
continue; continue;
}; };
const index = this.indexOf(cell) orelse continue; try q.insert(.{ .node_id = new_id, .coord = node.coord.add(.{ 0, -1 }) });
const hasVisited = this.levels[index] != 0; continue;
this.enable(cell);
if (hasVisited and !T.is_logic(tile)) continue;
count += 1;
if (get_logic(tile)) |logic| {
// TODO: implement other logic (though I'm pretty sure that requires a graph...)
if (logic != .And) continue;
if (this.levels[index] < 2) continue;
try q.insert(cell + util.Dir.up);
} }
for (get_outputs(tile)) |conductor, i| { for (get_outputs(tile)) |conductor, i| {
// w4.tracef("[fill] outputs");
if (!conductor) continue; if (!conductor) continue;
// w4.tracef("[fill] conductor");
const s = @intToEnum(Side, i); const s = @intToEnum(Side, i);
const delta = s.dir(); const delta = s.dir();
// TODO: check that cell can recieve from this side // TODO: check that cell can recieve from this side
const nextCell = cell + delta; const nextCoord = node.coord.addC(world.Coordinate.fromVec2(delta));
if (nextCell[0] < 0 or nextCell[1] < 0 or const tl = world.Coordinate.init(.{ 0, 0 });
nextCell[0] >= this.map_size[0] or const br = world.Coordinate.fromVec2(this.map_size);
nextCell[1] >= this.map_size[1]) // w4.tracef("[fill] next (%d, %d)", nextCoord.val[0], nextCoord.val[1]);
continue; // w4.tracef("[fill] range (%d, %d)-(%d, %d)", tl.val[0], tl.val[1], br.val[0], br.val[1]);
const nextTile = this.get_cell(nextCell) orelse here: { if (!nextCoord.within(tl, br)) continue;
for (this.doors.items) |*d| { // w4.tracef("[fill] within %d", nextCoord.within(tl, br));
if (@reduce(.And, d.cell == nextCell)) { const nextTile = this.getCoord(nextCoord) orelse 0;
d.enabled = true; // w4.tracef("[fill] nextTile");
if (get_inputs(nextTile)[@enumToInt(s.opposite())]) {
// w4.tracef("[fill] get_inputs");
try q.insert(.{
.node_id = node.node_id,
.coord = nextCoord,
});
} }
} }
break :here 0;
};
if (get_inputs(nextTile)[@enumToInt(s.opposite())])
try q.insert(nextCell);
}
if (T.is_plug(tile)) { if (T.is_plug(tile)) {
// w4.tracef("[fill] plug");
for (this.bridges.items) |*b| { for (this.bridges.items) |*b| {
if (@reduce(.And, b.cells[0] == cell)) { if (b.coords[0].eq(node.coord)) {
try q.insert(b.cells[1]); try q.insert(.{
.coord = b.coords[1],
.node_id = node.node_id,
});
b.enabled = true; b.enabled = true;
} else if (@reduce(.And, b.cells[1] == cell)) { } else if (b.coords[1].eq(node.coord)) {
try q.insert(b.cells[0]); try q.insert(.{
.coord = b.coords[0],
.node_id = node.node_id,
});
b.enabled = true; b.enabled = true;
} }
} }
} }
w4.tracef("[fill] end search step");
} }
return count; return count;
} }
@ -369,16 +395,18 @@ const tilemap_width = 16;
const tilemap_height = 16; const tilemap_height = 16;
const tilemap_stride = 128; const tilemap_stride = 128;
pub fn draw(this: @This(), offset: Vec2) void { pub fn draw(this: @This(), db: world.Database, offset: Vec2) void {
var y: usize = 0; var y: usize = 0;
while (y < height) : (y += 1) { while (y < height) : (y += 1) {
var x: usize = 0; var x: usize = 0;
while (x < width) : (x += 1) { while (x < width) : (x += 1) {
const cell = Vec2{ @intCast(i32, x), @intCast(i32, y) }; const cell = Vec2{ @intCast(i32, x), @intCast(i32, y) };
const pos = cell * tile_size; const pos = cell * tile_size;
const tile = this.get_cell(cell + offset) orelse continue; const coord = world.Coordinate.fromVec2(cell + offset);
const tile = this.getCoord(coord) orelse continue;
if (tile == 0) continue; if (tile == 0) continue;
if (this.isEnabled(cell + offset)) w4.DRAW_COLORS.* = 0x0210 else w4.DRAW_COLORS.* = 0x0310; const energized = if (this.getNodeID(coord)) |node| db.circuit_info[node].energized else false;
if (energized) w4.DRAW_COLORS.* = 0x0210 else w4.DRAW_COLORS.* = 0x0310;
const t = Vec2{ const t = Vec2{
@intCast(i32, (tile % tilemap_width) * tile_size[0]), @intCast(i32, (tile % tilemap_width) * tile_size[0]),
@intCast(i32, (tile / tilemap_width) * tile_size[0]), @intCast(i32, (tile / tilemap_width) * tile_size[0]),

View File

@ -18,6 +18,7 @@ pub const Options = struct {
plug: world.AutoTileset, plug: world.AutoTileset,
switch_on: world.AutoTileset, switch_on: world.AutoTileset,
switch_off: world.AutoTileset, switch_off: world.AutoTileset,
db: world.Database,
}; };
fn is_solid(tile: u7) bool { fn is_solid(tile: u7) bool {
@ -32,6 +33,7 @@ pub fn extractLevel(opt: Options) !void {
const alloc = opt.alloc; const alloc = opt.alloc;
const level = opt.level; const level = opt.level;
const tileset = opt.tileset; const tileset = opt.tileset;
const db = opt.db;
const tiles = level.tiles orelse return error.NullTiles; const tiles = level.tiles orelse return error.NullTiles;
@ -138,7 +140,18 @@ pub fn extractLevel(opt: Options) !void {
const y = @divTrunc(i, width); const y = @divTrunc(i, width);
const stride = width; const stride = width;
if (circuit_map[i] == .Source) circuit.addSource(.{@intCast(i32, x), @intCast(i32, y)}); if (circuit_map[i] == .Source) {
const levelc = world.Coordinate.fromVec2(.{ @intCast(i32, x), @intCast(i32, y) });
const coord = world.Coordinate.fromWorld(level.world_x, level.world_y).addC(levelc);
w4.tracef("[extract] source (%d, %d)", coord.val[0], coord.val[1]);
if (db.getNodeID(coord)) |node_id| {
circuit.addSource(.{
.coord = levelc,
.node_id = node_id,
});
w4.tracef("[extract] node id (%d)", node_id);
}
}
if (circuit_map[i] == .None) { if (circuit_map[i] == .None) {
autotiles[i] = null; autotiles[i] = null;

View File

@ -178,7 +178,7 @@ var ScoreCoin = Sprite{
var map_buf: [400]u8 = undefined; var map_buf: [400]u8 = undefined;
var circuit_lvl_buf: [400]u8 = undefined; var circuit_node_buf: [400]u8 = undefined;
var circuit_buf: [400]u8 = undefined; var circuit_buf: [400]u8 = undefined;
var circuit_options: Circuit.Options = undefined; var circuit_options: Circuit.Options = undefined;
@ -209,6 +209,8 @@ fn loadLevel(lvl: usize) !void {
level = try db.levelLoad(alloc, lvl); level = try db.levelLoad(alloc, lvl);
const levelc = world.Coordinate.fromWorld(level.world_x, level.world_y); const levelc = world.Coordinate.fromWorld(level.world_x, level.world_y);
w4.tracef("Loading level [%d] (%d, %d)", lvl, level.world_x, level.world_y);
try extract.extractLevel(.{ try extract.extractLevel(.{
.alloc = frame_alloc, .alloc = frame_alloc,
.level = level, .level = level,
@ -219,6 +221,7 @@ fn loadLevel(lvl: usize) !void {
.plug = world.Tiles.Plugs, .plug = world.Tiles.Plugs,
.switch_off = world.Tiles.SwitchesOff, .switch_off = world.Tiles.SwitchesOff,
.switch_on = world.Tiles.SwitchesOn, .switch_on = world.Tiles.SwitchesOn,
.db = db,
}); });
const tile_size = Vec2{ 8, 8 }; const tile_size = Vec2{ 8, 8 };
@ -258,7 +261,7 @@ fn loadLevel(lvl: usize) !void {
var i: usize = 0; var i: usize = 0;
while (db.getDoor(level, i)) |door| : (i += 1) { while (db.getDoor(level, i)) |door| : (i += 1) {
const coord = door.coord.subC(levelc); const coord = door.coord.subC(levelc);
try circuit.addDoor(coord.toVec2()); try circuit.addDoor(coord);
} }
} }
@ -269,7 +272,8 @@ fn loadLevel(lvl: usize) !void {
var e = false; var e = false;
if (db.isEnergized(globalc)) { if (db.isEnergized(globalc)) {
e = true; e = true;
circuit.addSource(.{ join.val[0], join.val[1] }); const node_id = db.getNodeID(globalc) orelse continue;
circuit.addSource(.{ .coord = join, .node_id = node_id });
} }
w4.tracef("---- Join %d: (%d, %d) <%d>", i, globalc.val[0], globalc.val[1], @boolToInt(e)); w4.tracef("---- Join %d: (%d, %d) <%d>", i, globalc.val[0], globalc.val[1], @boolToInt(e));
} }
@ -282,7 +286,7 @@ fn loadLevel(lvl: usize) !void {
var e = false; var e = false;
if (db.getSwitchState(globalc)) |state| { if (db.getSwitchState(globalc)) |state| {
e = true; e = true;
if (state != 0) circuit.switchOn(.{ _switch.val[0], _switch.val[1] }); if (state != 0) circuit.switchOn(levelc);
} }
w4.tracef("---- Switch %d: (%d, %d) <%d>", i, globalc.val[0], globalc.val[1], @boolToInt(e)); w4.tracef("---- Switch %d: (%d, %d) <%d>", i, globalc.val[0], globalc.val[1], @boolToInt(e));
} }
@ -378,10 +382,10 @@ pub fn start() !void {
circuit_options = .{ circuit_options = .{
.map = &circuit_buf, .map = &circuit_buf,
.levels = &circuit_lvl_buf, .nodes = &circuit_node_buf,
.map_size = level_size, .map_size = level_size,
.bridges = try alloc.alloc(Circuit.BridgeState, 5), .bridges = try alloc.alloc(Circuit.BridgeState, 5),
.sources = try alloc.alloc(util.Cell, 5), .sources = try alloc.alloc(Circuit.Source, 5),
.doors = try alloc.alloc(Circuit.DoorState, 10), .doors = try alloc.alloc(Circuit.DoorState, 10),
}; };
circuit = Circuit.init(circuit_options); circuit = Circuit.init(circuit_options);
@ -481,7 +485,7 @@ pub fn update(time: usize) !State {
camera = newCamera; camera = newCamera;
map.draw(camera); map.draw(camera);
circuit.draw(camera); circuit.draw(db, camera);
for (wires.slice()) |*wire| { for (wires.slice()) |*wire| {
wireDrawProcess(1, wire); wireDrawProcess(1, wire);
@ -503,12 +507,13 @@ pub fn update(time: usize) !State {
} }
{ {
const pos = util.world2cell(player.pos.pos); // const pos = util.world2cell(player.pos.pos);
const shouldHum = circuit.isEnabled(pos) or const shouldHum = false;
circuit.isEnabled(pos + util.Dir.up) or // circuit.isEnabled(pos) or
circuit.isEnabled(pos + util.Dir.down) or // circuit.isEnabled(pos + util.Dir.up) or
circuit.isEnabled(pos + util.Dir.left) or // circuit.isEnabled(pos + util.Dir.down) or
circuit.isEnabled(pos + util.Dir.right); // circuit.isEnabled(pos + util.Dir.left) or
// circuit.isEnabled(pos + util.Dir.right);
if (shouldHum) { if (shouldHum) {
w4.tone(.{ .start = 60 }, .{ .release = 255, .sustain = 0 }, 1, .{ .channel = .pulse1, .mode = .p50 }); w4.tone(.{ .start = 60 }, .{ .release = 255, .sustain = 0 }, 1, .{ .channel = .pulse1, .mode = .p50 });
} }
@ -588,7 +593,8 @@ const Interaction = struct {
fn getNearestCircuitInteraction(pos: Vec2f) ?Interaction { fn getNearestCircuitInteraction(pos: Vec2f) ?Interaction {
const cell = util.world2cell(pos); const cell = util.world2cell(pos);
if (circuit.get_cell(cell)) |tile| { const coord = Coord.fromVec2(cell);
if (circuit.getCoord(coord)) |tile| {
if (world.Tiles.is_switch(tile)) { if (world.Tiles.is_switch(tile)) {
return Interaction{ .details = .lever, .pos = cell * Map.tile_size + Vec2{ 4, 4 } }; return Interaction{ .details = .lever, .pos = cell * Map.tile_size + Vec2{ 4, 4 } };
} }
@ -598,9 +604,10 @@ fn getNearestCircuitInteraction(pos: Vec2f) ?Interaction {
fn getNearestPlugInteraction(pos: Vec2f, wireID: usize, which: usize) ?Interaction { fn getNearestPlugInteraction(pos: Vec2f, wireID: usize, which: usize) ?Interaction {
const cell = util.world2cell(pos); const cell = util.world2cell(pos);
if (circuit.get_cell(cell)) |tile| { const coord = world.Coordinate.fromVec2(cell);
if (circuit.getCoord(coord)) |tile| {
if (world.Tiles.is_plug(tile)) { if (world.Tiles.is_plug(tile)) {
const active = circuit.isEnabled(cell); const active = db.isEnergized(coord);
return Interaction{ return Interaction{
.details = .{ .plug = .{ .wireID = wireID, .which = which } }, .details = .{ .plug = .{ .wireID = wireID, .which = which } },
.pos = cell * Map.tile_size + Vec2{ 4, 4 }, .pos = cell * Map.tile_size + Vec2{ 4, 4 },
@ -706,17 +713,16 @@ fn manipulationProcess(pos: *Pos, control: *Control) !void {
}, },
.lever => { .lever => {
const cell = @divTrunc(i.pos, Map.tile_size); const cell = @divTrunc(i.pos, Map.tile_size);
const new_switch = circuit.toggle(cell); const coord = Coord.fromVec2(cell);
const new_switch = circuit.toggle(coord);
if (new_switch) |tile| { if (new_switch) |tile| {
const T = world.Tiles; const T = world.Tiles;
const new_state: u8 = switch (tile) { const new_state: u8 = switch (tile) {
T.SwitchTeeWestOn, T.SwitchTeeEastOn, T.SwitchVerticalOn => 1, T.SwitchTeeWestOn, T.SwitchTeeEastOn, T.SwitchVerticalOn => 1,
else => 0, else => 0,
}; };
const x = level.world_x * 20 + @intCast(i16, cell[0]);
const y = level.world_y * 20 + @intCast(i16, cell[1]);
db.setSwitch(Coord.init(.{ x, y }), new_state); db.setSwitch(coord.addC(Coord.fromWorld(level.world_x, level.world_y)), new_state);
} }
try updateCircuit(); try updateCircuit();
}, },
@ -731,37 +737,35 @@ fn updateCircuit() !void {
wire.enabled = false; wire.enabled = false;
if (!wire.begin().pinned or !wire.end().pinned) continue; if (!wire.begin().pinned or !wire.end().pinned) continue;
const nodes = wire.nodes.constSlice(); const nodes = wire.nodes.constSlice();
const cellBegin = util.world2cell(nodes[0].pos); const cellBegin = Coord.fromVec2(util.world2cell(nodes[0].pos));
const cellEnd = util.world2cell(nodes[nodes.len - 1].pos); const cellEnd = Coord.fromVec2(util.world2cell(nodes[nodes.len - 1].pos));
circuit.bridge(.{ cellBegin, cellEnd }, wireID); circuit.bridge(.{ cellBegin, cellEnd }, wireID);
const topleft = Coord.fromWorld(level.world_x, level.world_y); const topleft = Coord.fromWorld(level.world_x, level.world_y);
const p1 = Coord.init(.{ const globalBegin = cellBegin.addC(topleft);
@intCast(i16, cellBegin[0]), const globalEnd = cellEnd.addC(topleft);
@intCast(i16, cellBegin[1]),
}).addC(topleft);
const p2 = Coord.init(.{
@intCast(i16, cellEnd[0]),
@intCast(i16, cellEnd[1]),
}).addC(topleft);
db.connectPlugs(p1, p2) catch { db.connectPlugs(globalBegin, globalEnd) catch {
w4.tracef("connect plugs error"); w4.tracef("connect plugs error");
}; };
} }
try db.updateCircuit(frame_alloc);
// Simulate circuit // Simulate circuit
_ = try circuit.fill(frame_alloc); _ = try circuit.fill(frame_alloc, db, level);
w4.tracef("[updateCircuit] circuit filled");
// for (circuit.nodes) |node, i| {
// w4.tracef("%d: %d", i, node);
// }
// Energize wires // Energize wires
for (wires.slice()) |*wire| { {
const begin = wire.begin(); var i: usize = 0;
const end = wire.end(); while (circuit.enabledBridge(i)) |wireID| : (i += 1) {
const cellBegin = util.world2cell(begin.pos); wires.slice()[wireID].enabled = true;
const cellEnd = util.world2cell(end.pos); }
if ((circuit.isEnabled(cellBegin) and begin.pinned) or
(circuit.isEnabled(cellEnd) and end.pinned)) wire.enabled = true;
} }
// Add doors to map // Add doors to map
@ -770,27 +774,22 @@ fn updateCircuit() !void {
const tile: u8 = if (door.kind == .Door) world.Tiles.Door else world.Tiles.Trapdoor; const tile: u8 = if (door.kind == .Door) world.Tiles.Door else world.Tiles.Trapdoor;
const globalc = world.Coordinate.fromWorld(level.world_x, level.world_y); const globalc = world.Coordinate.fromWorld(level.world_x, level.world_y);
const coord = door.coord.subC(globalc); const coord = door.coord.subC(globalc);
w4.tracef("[getDoor] (%d, %d)", coord.val[0], coord.val[1]); if (db.isEnergized(door.coord)) {
w4.tracef("[door] open (%d, %d)", door.coord.val[0], door.coord.val[1]);
try map.set_cell(coord.toVec2(), world.Tiles.Empty);
} else {
w4.tracef("[door] closed (%d, %d)", door.coord.val[0], door.coord.val[1]);
try map.set_cell(coord.toVec2(), tile); try map.set_cell(coord.toVec2(), tile);
} }
// Remove doors that have been unlocked
const enabledDoors = try circuit.enabledDoors(frame_alloc);
defer frame_alloc.free(enabledDoors.items);
for (enabledDoors.items) |door| {
w4.tracef("[enabledDoors] (%d, %d)", door[0], door[1]);
try map.set_cell(door, world.Tiles.Empty);
} }
try db.updateCircuit(frame_alloc);
for (db.circuit_info) |node, n| { for (db.circuit_info) |node, n| {
const e = @boolToInt(node.energized); const e = @boolToInt(node.energized);
switch (node.kind) { switch (node.kind) {
.Conduit => |Conduit| w4.tracef("[%d]: Conduit [%d, %d] <%d>", n, Conduit[0], Conduit[1], e), .Conduit => |Conduit| w4.tracef("[%d]: Conduit [%d, %d] <%d>", n, Conduit[0], Conduit[1], e),
.And => |And| w4.tracef("[%d]: And [%d, %d] <%d>", n, And[0], And[1], e), .And => |And| w4.tracef("[%d]: And [%d, %d] <%d>", n, And[0], And[1], e),
.Xor => |Xor| w4.tracef("[%d]: Xor [%d, %d] <%d>", n, Xor[0], Xor[1], e), .Xor => |Xor| w4.tracef("[%d]: Xor [%d, %d] <%d>", n, Xor[0], Xor[1], e),
.Source => w4.tracef("[%d]: Source", n), .Source => w4.tracef("[%d]: Source (%d, %d)", n, node.coord.val[0], node.coord.val[1]),
.Socket => |Socket| { .Socket => |Socket| {
const socket = Socket orelse std.math.maxInt(world.NodeID); const socket = Socket orelse std.math.maxInt(world.NodeID);
w4.tracef("[%d]: Socket [%d] <%d>", n, socket, e); w4.tracef("[%d]: Socket [%d] <%d>", n, socket, e);

View File

@ -47,9 +47,9 @@ pub const Tiles = struct {
return tile >= 16 and tile < 20; return tile >= 16 and tile < 20;
} }
pub const LogicAnd = 21; pub const LogicAnd = 20;
pub const LogicNot = 22; pub const LogicNot = 21;
pub const LogicXor = 23; pub const LogicXor = 22;
pub fn is_logic(tile: u8) bool { pub fn is_logic(tile: u8) bool {
return tile >= 21 and tile <= 24; return tile >= 21 and tile <= 24;
@ -753,7 +753,7 @@ pub const Database = struct {
// Circuit functions // Circuit functions
fn getNodeID(db: *Database, coord: Coord) ?NodeID { pub fn getNodeID(db: Database, coord: Coord) ?NodeID {
for (db.circuit_info) |node, i| { for (db.circuit_info) |node, i| {
if (!coord.eq(node.coord)) continue; if (!coord.eq(node.coord)) continue;
return @intCast(NodeID, i); return @intCast(NodeID, i);
@ -761,6 +761,11 @@ pub const Database = struct {
return null; return null;
} }
pub fn getLevelNodeID(db: Database, level: Level, coord: Coord) ?NodeID {
const levelc = Coord.fromWorld(level.world_x, level.world_y);
return db.getNodeID(coord.addC(levelc));
}
pub fn connectPlugs(db: *Database, p1: Coord, p2: Coord) !void { pub fn connectPlugs(db: *Database, p1: Coord, p2: Coord) !void {
const p1id = db.getNodeID(p1) orelse return; const p1id = db.getNodeID(p1) orelse return;
const p2id = db.getNodeID(p2) orelse return; const p2id = db.getNodeID(p2) orelse return;

View File

@ -480,17 +480,18 @@ pub fn buildCircuit(alloc: std.mem.Allocator, levels: []world.Level) !std.ArrayL
// Add switch outlets // Add switch outlets
if (input_dir != .West and west) { if (input_dir != .West and west) {
const out_node = @intCast(world.NodeID, nodes.items.len); const out_node = @intCast(world.NodeID, nodes.items.len);
const new_coord = coord.add(.{1, 0});
try nodes.append(.{ try nodes.append(.{
.kind = .{ .SwitchOutlet = .{ .kind = .{ .SwitchOutlet = .{
.source = next_node, .source = next_node,
.which = 0, .which = 0,
} }, } },
.coord = coord, .coord = new_coord,
}); });
const right = try alloc.create(Node); const right = try alloc.create(Node);
right.* = Node{ .data = .{ right.* = Node{ .data = .{
.last_node = out_node, .last_node = out_node,
.coord = coord.add(.{ 1, 0 }), .coord = new_coord,
.last_coord = coord, .last_coord = coord,
} }; } };
bfs_queue.append(right); bfs_queue.append(right);
@ -498,17 +499,18 @@ pub fn buildCircuit(alloc: std.mem.Allocator, levels: []world.Level) !std.ArrayL
if (input_dir != .East and east) { if (input_dir != .East and east) {
const out_node = @intCast(world.NodeID, nodes.items.len); const out_node = @intCast(world.NodeID, nodes.items.len);
const new_coord = coord.add(.{-1, 0});
try nodes.append(.{ try nodes.append(.{
.kind = .{ .SwitchOutlet = .{ .kind = .{ .SwitchOutlet = .{
.source = next_node, .source = next_node,
.which = 0, .which = 0,
} }, } },
.coord = coord, .coord = new_coord,
}); });
const left = try alloc.create(Node); const left = try alloc.create(Node);
left.* = Node{ .data = .{ left.* = Node{ .data = .{
.last_node = out_node, .last_node = out_node,
.coord = coord.add(.{ -1, 0 }), .coord = new_coord,
.last_coord = coord, .last_coord = coord,
} }; } };
bfs_queue.append(left); bfs_queue.append(left);
@ -516,17 +518,18 @@ pub fn buildCircuit(alloc: std.mem.Allocator, levels: []world.Level) !std.ArrayL
if (input_dir != .South and south) { if (input_dir != .South and south) {
const out_node = @intCast(world.NodeID, nodes.items.len); const out_node = @intCast(world.NodeID, nodes.items.len);
const new_coord = coord.add(.{0, 1});
try nodes.append(.{ try nodes.append(.{
.kind = .{ .SwitchOutlet = .{ .kind = .{ .SwitchOutlet = .{
.source = next_node, .source = next_node,
.which = 1, .which = 1,
} }, } },
.coord = coord, .coord = new_coord,
}); });
const down = try alloc.create(Node); const down = try alloc.create(Node);
down.* = Node{ .data = .{ down.* = Node{ .data = .{
.last_node = out_node, .last_node = out_node,
.coord = coord.add(.{ 0, 1 }), .coord = new_coord,
.last_coord = coord, .last_coord = coord,
} }; } };
bfs_queue.append(down); bfs_queue.append(down);
@ -534,17 +537,18 @@ pub fn buildCircuit(alloc: std.mem.Allocator, levels: []world.Level) !std.ArrayL
if (input_dir != .North and north) { if (input_dir != .North and north) {
const out_node = @intCast(world.NodeID, nodes.items.len); const out_node = @intCast(world.NodeID, nodes.items.len);
const new_coord = coord.add(.{0, -1});
try nodes.append(.{ try nodes.append(.{
.kind = .{ .SwitchOutlet = .{ .kind = .{ .SwitchOutlet = .{
.source = next_node, .source = next_node,
.which = 1, .which = 1,
} }, } },
.coord = coord, .coord = new_coord,
}); });
const up = try alloc.create(Node); const up = try alloc.create(Node);
up.* = Node{ .data = .{ up.* = Node{ .data = .{
.last_node = out_node, .last_node = out_node,
.coord = coord.add(.{ 0, -1 }), .coord = new_coord,
.last_coord = coord, .last_coord = coord,
} }; } };
bfs_queue.append(up); bfs_queue.append(up);
@ -695,21 +699,5 @@ pub fn buildCircuit(alloc: std.mem.Allocator, levels: []world.Level) !std.ArrayL
} }
} }
var i: usize = 0;
while (i < nodes.items.len) : (i += 1) {
switch (nodes.items[i].kind) {
// .Source => {
// },
.And => {},
.Xor => {},
.Conduit => {},
.Plug => {},
.Switch => {},
.Join => {},
.Outlet => {},
else => {},
}
}
return nodes; return nodes;
} }