diff --git a/src/circuit.zig b/src/circuit.zig index 8f34381..846dbb2 100644 --- a/src/circuit.zig +++ b/src/circuit.zig @@ -230,14 +230,16 @@ pub fn isEnabled(this: @This(), cell: Cell) bool { return this.levels[i] >= 1; } -pub fn toggle(this: *@This(), c: Cell) void { +pub fn toggle(this: *@This(), c: Cell) ?u8 { const cell = c; if (this.get_cell(cell)) |tile| { if (T.is_switch(tile)) { const toggled = toggle_switch(tile); this.set_cell(cell, toggled); + return tile; } } + return null; } pub fn clearMap(this: *@This()) void { diff --git a/src/game.zig b/src/game.zig index 7859b07..c5bbf7c 100644 --- a/src/game.zig +++ b/src/game.zig @@ -651,7 +651,22 @@ fn manipulationProcess(pos: *Pos, control: *Control) !void { }, .lever => { const cell = @divTrunc(i.pos, Map.tile_size); - circuit.toggle(cell); + const new_switch = circuit.toggle(cell); + if (new_switch) |tile| { + const T = world.Tiles; + // TODO: make switch system better + const new_state: world.NodeKind.SwitchEnum = switch(tile) { + T.SwitchTeeWestOn => .North, + T.SwitchTeeWestOff => .West, + T.SwitchTeeEastOn => .North, + T.SwitchTeeEastOff => .East, + T.SwitchVerticalOn => .South, + else => .Off, + }; + 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); + } try updateCircuit(); }, } @@ -728,7 +743,8 @@ fn updateCircuit() !void { w4.tracef("[%d]: Socket [%d] <%d>", n, socket, e); }, .Plug => |Plug| w4.tracef("[%d]: Plug [%d] <%d>", n, Plug, e), - .Switch => |Switch| w4.tracef("[%d]: Switch [%s] <%d>", n, &@tagName(Switch), e), + .Switch => |Switch| w4.tracef("[%d]: Switch %d [%d] <%d>", n, Switch.state, Switch.source, e), + .SwitchOutlet => |Switch| w4.tracef("[%d]: SwitchOutlet %d [%d] <%d>", n, Switch.which, Switch.source, e), .Join => |Join| w4.tracef("[%d]: Join [%d] <%d>", n, Join, e), .Outlet => |Outlet| w4.tracef("[%d]: Outlet [%d] <%d>", n, Outlet, e), } diff --git a/src/world.zig b/src/world.zig index 9946e93..b4c6eb2 100644 --- a/src/world.zig +++ b/src/world.zig @@ -116,6 +116,16 @@ pub const TileData = union(enum) { circuit: CircuitType, }, + pub fn getCircuit(data: TileData) ?CircuitType { + switch (data) { + .tile => |_| return null, + .flags => |flags| { + if (flags.circuit == .None) return null; + return flags.circuit; + }, + } + } + pub fn toByte(data: TileData) u8 { switch (data) { .tile => |int| return 0b1000_0000 | @intCast(u8, int), @@ -665,6 +675,12 @@ pub const Database = struct { } } + + pub fn setSwitch(db: *Database, coord: Coord, new_state: NodeKind.SwitchEnum) void { + const _switch = db.getNodeID(coord) orelse return; + db.circuit_info[_switch].kind.Switch.state = new_state; + } + pub fn isEnergized(db: *Database, coord: Coord) bool { for (db.circuit_info) |node, i| { if (!coord.eq(node.coord)) continue; @@ -702,10 +718,16 @@ pub const Database = struct { .Plug => |Plug| { db.circuit_info[i].energized = db.circuit_info[Plug].energized; }, - .Switch => |state| { - // TODO Rework switch to make sense - db.circuit_info[i].energized = false; - _ = state; + .Switch => |_Switch| { + db.circuit_info[i].energized = db.circuit_info[_Switch.source].energized; + }, + .SwitchOutlet => |_Switch| { + const _switch = db.circuit_info[_Switch.source]; + const _outlet = db.circuit_info[i].kind.SwitchOutlet; + // If the switch isn't energized, this outlet is not energized + if (!_switch.energized) db.circuit_info[i].energized = false; + // If the switch is energized, check that it is outputting to this outlet + db.circuit_info[i].energized = _outlet.which == _switch.kind.Switch.state; }, .Join => |Join| { db.circuit_info[i].energized = db.circuit_info[Join].energized; @@ -772,6 +794,7 @@ const NodeEnum = enum(u4) { Plug, Socket, Switch, + SwitchOutlet, Join, Outlet, }; @@ -798,11 +821,25 @@ pub const NodeKind = union(NodeEnum) { /// Vertical = Off or Top/Bottom, depending on flow /// Horizontal = Off or Left/Right, depending on flow /// Tee = Top/Bottom or Left/Right, depending on flow - Switch: SwitchEnum, + Switch: Switch, + /// Interface between a switch and other components. Each one + /// of these represents a possible outlet on a switch, and reads + /// the state of the switch to determine if it is powered or not + SwitchOutlet: SwitchOutlet, + /// Connection between levels Join: NodeID, + /// Used to identify entities that recieve power, like doors Outlet: NodeID, - const SwitchEnum = enum { Off, Bottom, Top, Left, Right }; + pub const Switch = struct { + source: NodeID, + state: SwitchEnum, + }; + pub const SwitchOutlet = struct { + source: NodeID, + which: SwitchEnum, + }; + pub const SwitchEnum = enum { Off, North, West, East, South }; pub fn read(reader: anytype) !NodeKind { var kind: NodeKind = undefined; @@ -839,9 +876,16 @@ pub const NodeKind = union(NodeEnum) { kind = .{ .Plug = try reader.readInt(NodeID, .Little) }; }, .Switch => { - kind = .{ - .Switch = @intToEnum(SwitchEnum, try reader.readInt(NodeID, .Little)), - }; + kind = .{ .Switch = .{ + .source = try reader.readInt(NodeID, .Little), + .state = @intToEnum(SwitchEnum, try reader.readInt(u8, .Little)), + } }; + }, + .SwitchOutlet => { + kind = .{ .SwitchOutlet = .{ + .source = try reader.readInt(NodeID, .Little), + .which = @intToEnum(SwitchEnum, try reader.readInt(u8, .Little)), + } }; }, .Join => { kind = .{ .Join = try reader.readInt(NodeID, .Little) }; @@ -876,8 +920,13 @@ pub const NodeKind = union(NodeEnum) { const socket = Socket orelse std.math.maxInt(NodeID); try writer.writeInt(NodeID, socket, .Little); }, - .Switch => |Switch| { - try writer.writeInt(NodeID, @enumToInt(Switch), .Little); + .Switch => |_Switch| { + try writer.writeInt(NodeID, _Switch.source, .Little); + try writer.writeInt(u8, @enumToInt(_Switch.state), .Little); + }, + .SwitchOutlet => |_Switch| { + try writer.writeInt(NodeID, _Switch.source, .Little); + try writer.writeInt(u8, @enumToInt(_Switch.which), .Little); }, .Join => |Join| { try writer.writeInt(NodeID, Join, .Little); @@ -899,7 +948,8 @@ pub const NodeKind = union(NodeEnum) { .Source => std.fmt.format(writer, "{s}", .{name}), .Plug => |Plug| std.fmt.format(writer, "{s} [{}]", .{ name, Plug }), .Socket => |Socket| std.fmt.format(writer, "{s} [{?}]", .{ name, Socket }), - .Switch => |Switch| std.fmt.format(writer, "{s} [{s}]", .{ name, @tagName(Switch) }), + .Switch => |_Switch| std.fmt.format(writer, "{s} <{s}> [{}]", .{ name, @tagName(_Switch.state), _Switch.source }), + .SwitchOutlet => |_Switch| std.fmt.format(writer, "{s} <{s}> [{}]", .{ name, @tagName(_Switch.which), _Switch.source }), .Join => |Join| std.fmt.format(writer, "{s} [{}]", .{ name, Join }), .Outlet => |Outlet| std.fmt.format(writer, "{s} [{}]", .{ name, Outlet }), }; diff --git a/tools/LDtkImport.zig b/tools/LDtkImport.zig index f58a5b7..cd968b7 100644 --- a/tools/LDtkImport.zig +++ b/tools/LDtkImport.zig @@ -367,7 +367,9 @@ pub fn buildCircuit(alloc: std.mem.Allocator, levels: []world.Level) !std.ArrayL switch (flags.circuit) { .Source => {}, // Do nothing, but add everything around the source .Conduit => { - // Collects from two other nodes. Needs to store more info in coordinate queue + // Collects from two other nodes. Intersections will need to be stored so when + // we find out we have to outputs, we can add the conduit and possible rewrite + // previous nodes to point to the conduit // TODO }, .Socket => { @@ -393,21 +395,139 @@ pub fn buildCircuit(alloc: std.mem.Allocator, levels: []world.Level) !std.ArrayL .coord = coord, }); }, - .Switch_Off => { - // TODO: Find last coordinate of search and determine flow + .Switch_Off, .Switch_On => { + // Identify input side + const last_coord = node.data.last_coord.?; + const Dir = enum { North, West, East, South }; + const input_dir: Dir = dir: { + if (last_coord.eq(coord.add(.{ 0, -1 }))) { + break :dir .North; + } else if (last_coord.eq(coord.add(.{ -1, 0 }))) { + break :dir .West; + } else if (last_coord.eq(coord.add(.{ 1, 0 }))) { + break :dir .East; + } else { + break :dir .South; + } + }; + // Find outlets + const north_opt = level.?.getTile(coord.add(.{ 0, -1 })).?.getCircuit(); + const west_opt = level.?.getTile(coord.add(.{ -1, 0 })).?.getCircuit(); + const east_opt = level.?.getTile(coord.add(.{ 1, 0 })).?.getCircuit(); + const south_opt = level.?.getTile(coord.add(.{ 0, 1 })).?.getCircuit(); + + const north = (north_opt orelse world.CircuitType.None) != .None; + const west = (west_opt orelse world.CircuitType.None) != .None; + const east = (east_opt orelse world.CircuitType.None) != .None; + const south = (south_opt orelse world.CircuitType.None) != .None; + + // We don't have four way switches, don't allow them + std.debug.assert(west != true or east != true); + // We only have vertically oriented switches ATM + std.debug.assert(north == true and south == true); + + // Determine initial state of switch + const state: world.NodeKind.SwitchEnum = state: { + // Vertical switch + if (!west and !east) { + if (flags.circuit == .Switch_Off) break :state .Off; + if (input_dir == .North) break :state .South; + break :state .North; + } + if (east and !west) { + if (flags.circuit == .Switch_Off) break :state .East; + if (input_dir == .North) break :state .South; + break :state .North; + } + if (west and !east) { + if (flags.circuit == .Switch_Off) break :state .West; + if (input_dir == .North) break :state .South; + break :state .North; + } + return error.ImpossibleSwitchState; + }; + // Add switch next_node = @intCast(world.NodeID, nodes.items.len); try nodes.append(.{ - .kind = .{ .Switch = .Off }, - .coord = coord, - }); - }, - .Switch_On => { - // TODO: Find last coordinate of search and determine flow - next_node = @intCast(world.NodeID, nodes.items.len); - try nodes.append(.{ - .kind = .{ .Switch = .Off }, + .kind = .{ .Switch = .{ + .source = last_node, + .state = state, + } }, .coord = coord, }); + // Add switch outlets + if (input_dir != .West and west) { + const out_node = @intCast(world.NodeID, nodes.items.len); + try nodes.append(.{ + .kind = .{ .SwitchOutlet = .{ + .source = next_node, + .which = .West, + } }, + .coord = coord, + }); + const right = try alloc.create(Node); + right.* = Node{ .data = .{ + .last_node = out_node, + .coord = coord.add(.{ 1, 0 }), + .last_coord = coord, + } }; + bfs_queue.append(right); + } + + if (input_dir != .East and east) { + const out_node = @intCast(world.NodeID, nodes.items.len); + try nodes.append(.{ + .kind = .{ .SwitchOutlet = .{ + .source = next_node, + .which = .East, + } }, + .coord = coord, + }); + const left = try alloc.create(Node); + left.* = Node{ .data = .{ + .last_node = out_node, + .coord = coord.add(.{ -1, 0 }), + .last_coord = coord, + } }; + bfs_queue.append(left); + } + + if (input_dir != .South and south) { + const out_node = @intCast(world.NodeID, nodes.items.len); + try nodes.append(.{ + .kind = .{ .SwitchOutlet = .{ + .source = next_node, + .which = .South, + } }, + .coord = coord, + }); + const down = try alloc.create(Node); + down.* = Node{ .data = .{ + .last_node = out_node, + .coord = coord.add(.{ 0, 1 }), + .last_coord = coord, + } }; + bfs_queue.append(down); + } + + if (input_dir != .North and north) { + const out_node = @intCast(world.NodeID, nodes.items.len); + try nodes.append(.{ + .kind = .{ .SwitchOutlet = .{ + .source = next_node, + .which = .North, + } }, + .coord = coord, + }); + const up = try alloc.create(Node); + up.* = Node{ .data = .{ + .last_node = out_node, + .coord = coord.add(.{ 0, -1 }), + .last_coord = coord, + } }; + bfs_queue.append(up); + } + continue; }, .Join => { const last_coord = node.data.last_coord.?;