diff --git a/src/circuit.zig b/src/circuit.zig index 3c05bea..4d0c4d1 100644 --- a/src/circuit.zig +++ b/src/circuit.zig @@ -202,15 +202,15 @@ bridges: std.BoundedArray(BridgeState, MAXBRIDGES), sources: std.BoundedArray(Cell, MAXSOURCES), doors: std.BoundedArray(DoorState, MAXDOORS), -pub fn init(map: []u8, levels: []u8, map_size: Vec2) @This() { +pub fn init(map: []u8, levels: []u8, map_size: Vec2) !@This() { std.debug.assert(map.len == levels.len); var this = @This(){ .map = map, .levels = levels, .map_size = map_size, - .bridges = std.BoundedArray(BridgeState, MAXBRIDGES).init(0) catch unreachable, - .sources = std.BoundedArray(Cell, MAXSOURCES).init(0) catch unreachable, - .doors = std.BoundedArray(DoorState, MAXDOORS).init(0) catch unreachable, + .bridges = try std.BoundedArray(BridgeState, MAXBRIDGES).init(0), + .sources = try std.BoundedArray(Cell, MAXSOURCES).init(0), + .doors = try std.BoundedArray(DoorState, MAXDOORS).init(0), }; return this; } @@ -225,38 +225,38 @@ 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])) |_| { - this.bridges.append(.{ .cells = cells, .id = bridgeID, .enabled = false }) catch unreachable; + try 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)) |_| { - this.sources.append(cell) catch unreachable; + try this.sources.append(cell); } } -pub fn addDoor(this: *@This(), cell: Cell) void { +pub fn addDoor(this: *@This(), cell: Cell) !void { if (this.indexOf(cell)) |_| { - this.doors.append(.{ .cell = cell, .enabled = false }) catch unreachable; + try this.doors.append(.{ .cell = cell, .enabled = false }); } } -pub fn enabledBridges(this: @This()) std.BoundedArray(usize, MAXBRIDGES) { - var items = std.BoundedArray(usize, MAXBRIDGES).init(0) catch unreachable; +pub fn enabledBridges(this: @This()) !std.BoundedArray(usize, MAXBRIDGES) { + var items = try std.BoundedArray(usize, MAXBRIDGES).init(0); for (this.bridges.constSlice()) |b| { - if (b.enabled) items.append(b.id) catch unreachable; + if (b.enabled) try items.append(b.id); } return items; } -pub fn enabledDoors(this: @This()) std.BoundedArray(Cell, MAXDOORS) { - var items = std.BoundedArray(Cell, MAXDOORS).init(0) catch unreachable; +pub fn enabledDoors(this: @This()) !std.BoundedArray(Cell, MAXDOORS) { + var items = try std.BoundedArray(Cell, MAXDOORS).init(0); for (this.doors.constSlice()) |d| { - if (d.enabled) items.append(d.cell) catch unreachable; + if (d.enabled) try items.append(d.cell); } return items; } @@ -281,23 +281,25 @@ pub fn clear(this: *@This()) void { for (this.doors.slice()) |*door| { door.enabled = false; } + // Resizing to zero should always work this.bridges.resize(0) catch unreachable; } pub fn reset(this: *@This()) void { this.clear(); + // Resizing to zero should always work this.sources.resize(0) catch unreachable; } const w4 = @import("wasm4.zig"); const Queue = util.Queue(Cell, MAXCELLS); // Returns number of cells filled -pub fn fill(this: *@This()) usize { +pub fn fill(this: *@This()) !usize { var count: usize = 0; - var visited = std.BoundedArray(usize, MAXCELLS).init(0) catch unreachable; - var q = Queue.init(); + var visited = try std.BoundedArray(usize, MAXCELLS).init(0); + var q = try Queue.init(); for (this.sources.slice()) |source| { - q.insert(source); + try q.insert(source); } while (q.remove()) |cell| { const tile = this.get_cell(cell) orelse { @@ -312,13 +314,13 @@ pub fn fill(this: *@This()) usize { this.enable(cell); const hasVisited = std.mem.containsAtLeast(usize, visited.slice(), 1, &.{index}); if (hasVisited and !is_logic(tile)) continue; - visited.append(index) catch unreachable; + try visited.append(index); 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; - q.insert(cell + util.Dir.up); + try q.insert(cell + util.Dir.up); } for (get_outputs(tile)) |conductor, i| { if (!conductor) continue; @@ -339,15 +341,15 @@ pub fn fill(this: *@This()) usize { break :here 0; }; if (get_inputs(nextTile)[@enumToInt(s.opposite())]) - q.insert(nextCell); + try q.insert(nextCell); } if (is_plug(tile)) { for (this.bridges.slice()) |*b| { if (@reduce(.And, b.cells[0] == cell)) { - q.insert(b.cells[1]); + try q.insert(b.cells[1]); b.enabled = true; } else if (@reduce(.And, b.cells[1] == cell)) { - q.insert(b.cells[0]); + try q.insert(b.cells[0]); b.enabled = true; } } diff --git a/src/disk.zig b/src/disk.zig index e1f6b84..0d9976b 100644 --- a/src/disk.zig +++ b/src/disk.zig @@ -58,7 +58,7 @@ pub fn reset() void { // so a player can see how well they've done with the game in the past. } -pub fn load() bool { +pub fn load() !bool { var load_buf: [1024]u8 = undefined; const read = w4.diskr(&load_buf, 1024); w4.tracef("%d bytes read", read); @@ -96,12 +96,12 @@ pub fn load() bool { // player.pos.pos += Vec2f{ 4, 6 }; }, .Coin => { - game.coins.append(.{ + try game.coins.append(.{ .pos = pos, .sprite = .{ .offset = .{ 0, 0 }, .size = .{ 8, 8 }, .index = 4, .flags = .{ .bpp = .b2 } }, .anim = Anim{ .anim = &game.anim_store.coin }, .area = .{ .pos = .{ 0, 0 }, .size = .{ 8, 8 } }, - }) catch unreachable; + }); }, .WireBeginPinned => { var begin = game.wires.slice()[id].begin(); diff --git a/src/game.zig b/src/game.zig index bf2078a..95b29d7 100644 --- a/src/game.zig +++ b/src/game.zig @@ -71,24 +71,24 @@ const Particle = struct { const ParticleSystem = struct { const MAXPARTICLES = 32; particles: std.BoundedArray(Particle, MAXPARTICLES), - pub fn init() @This() { + pub fn init() !@This() { return @This(){ - .particles = std.BoundedArray(Particle, MAXPARTICLES).init(0) catch unreachable, + .particles = try std.BoundedArray(Particle, MAXPARTICLES).init(0), }; } - pub fn update(this: *@This()) void { + pub fn update(this: *@This()) !void { var physics = .{ .gravity = Vec2f{ 0, 0.1 }, .friction = Vec2f{ 0.1, 0.1 } }; - var remove = std.BoundedArray(usize, MAXPARTICLES).init(0) catch unreachable; + var remove = try std.BoundedArray(usize, MAXPARTICLES).init(0); for (this.particles.slice()) |*part, i| { if (!inView(part.pos.pos)) { - remove.append(i) catch unreachable; + try remove.append(i); continue; } velocityProcess(1, &part.pos); physicsProcess(1, &part.pos, &physics); part.life -= 1; - if (part.life == 0) remove.append(i) catch unreachable; + if (part.life == 0) try remove.append(i); } while (remove.popOrNull()) |i| { _ = this.particles.swapRemove(i); @@ -108,7 +108,9 @@ const ParticleSystem = struct { const posComp = Pos.initVel(pos, vel); const life = randRange(10, 50); const part = Particle.init(posComp, life); - this.particles.append(part) catch unreachable; + // Do nothing on error, we don't care if a particle + // is dropped + this.particles.append(part) catch {}; } pub fn createNRandom(this: *@This(), pos: Vec2f, n: usize) void { @@ -182,11 +184,11 @@ fn showErr(msg: []const u8) noreturn { unreachable; } -pub fn start() void { - particles = ParticleSystem.init(); +pub fn start() !void { + particles = try ParticleSystem.init(); std.mem.set(u8, &conduitLevels_mutable, 0); - circuit = Circuit.init(&conduit_mutable, &conduitLevels_mutable, assets.conduit_size); + circuit = try Circuit.init(&conduit_mutable, &conduitLevels_mutable, assets.conduit_size); map = Map.init(&solids_mutable, assets.solid_size); camera = @divTrunc(assets.spawn, @splat(2, @as(i32, 20))) * @splat(2, @as(i32, 20)); @@ -223,15 +225,15 @@ pub fn start() void { } for (assets.sources) |source| { - circuit.addSource(source); + try circuit.addSource(source); } for (assets.doors) |door| { - circuit.addDoor(door); + try circuit.addDoor(door); } // _ = w4.diskw("", 0); - if (!Disk.load()) { + if (!try Disk.load()) { for (assets.coins) |coin| { coins.append(.{ .pos = Pos.init(util.vec2ToVec2f(coin * tile_size)), @@ -242,14 +244,14 @@ pub fn start() void { } } - updateCircuit(); + try updateCircuit(); } var indicator: ?Interaction = null; -pub fn update(time: usize) State { +pub fn update(time: usize) !State { for (wires.slice()) |*wire| { - wirePhysicsProcess(1, wire); + try wirePhysicsProcess(1, wire); if (wire.enabled) { if (music.isDrumBeat()) { if (!wire.begin().pinned) particles.createNRandom(wire.begin().pos, 8); @@ -260,11 +262,11 @@ pub fn update(time: usize) State { velocityProcess(1, &player.pos); physicsProcess(1, &player.pos, &player.physics); - manipulationProcess(&player.pos, &player.control); + try manipulationProcess(&player.pos, &player.control); controlProcess(1, &player.pos, &player.control, &player.physics, &player.kinematic); - kinematicProcess(1, &player.pos, &player.kinematic); + try kinematicProcess(1, &player.pos, &player.kinematic); controlAnimProcess(1, &player.sprite, &player.controlAnim, &player.control); - particles.update(); + try particles.update(); // Drawing w4.DRAW_COLORS.* = 0x0004; @@ -273,13 +275,13 @@ pub fn update(time: usize) State { { var shouldSave = false; - var remove = std.BoundedArray(usize, 10).init(0) catch unreachable; + var remove = try std.BoundedArray(usize, 10).init(0); for (coins.slice()) |*coin, i| { staticAnimProcess(1, &coin.sprite, &coin.anim); drawProcess(1, &coin.pos, &coin.sprite); if (coin.area.addv(coin.pos.pos).overlaps(player.kinematic.col.addv(player.pos.pos))) { score += 1; - remove.append(i) catch unreachable; + try remove.append(i); music.playCollect(score); shouldSave = true; } @@ -370,7 +372,7 @@ pub fn update(time: usize) State { } // Music - const musicCommand = music.getNext(1); + const musicCommand = try music.getNext(1); for (musicCommand.constSlice()) |sfx| { w4.tone(sfx.freq, sfx.duration, sfx.volume, sfx.flags); } @@ -442,7 +444,7 @@ fn getNearestWireInteraction(pos: Vec2f, range: f32) ?Interaction { return newIndicator; } -fn manipulationProcess(pos: *Pos, control: *Control) void { +fn manipulationProcess(pos: *Pos, control: *Control) !void { var offset = switch (control.facing) { .left => Vec2f{ -6, 0 }, .right => Vec2f{ 6, 0 }, @@ -495,25 +497,25 @@ fn manipulationProcess(pos: *Pos, control: *Control) void { control.grabbing = .{ .id = wire.id, .which = wire.which }; wires.slice()[wire.id].nodes.slice()[wire.which].pos = pos.pos + Vec2f{ 0, -4 }; wires.slice()[wire.id].nodes.slice()[wire.which].pinned = false; - updateCircuit(); + try updateCircuit(); }, .plug => |plug| { wires.slice()[plug.wireID].nodes.slice()[plug.which].pos = vec2tovec2f(indicator.?.pos); wires.slice()[plug.wireID].nodes.slice()[plug.which].pinned = true; control.grabbing = null; - updateCircuit(); + try updateCircuit(); }, .lever => { const cell = @divTrunc(i.pos, Map.tile_size); circuit.toggle(cell); - updateCircuit(); + try updateCircuit(); }, } } } } -fn updateCircuit() void { +fn updateCircuit() !void { circuit.clear(); for (wires.slice()) |*wire, wireID| { wire.enabled = false; @@ -522,9 +524,9 @@ fn updateCircuit() void { const cellBegin = util.world2cell(nodes[0].pos); const cellEnd = util.world2cell(nodes[nodes.len - 1].pos); - circuit.bridge(.{ cellBegin, cellEnd }, wireID); + try circuit.bridge(.{ cellBegin, cellEnd }, wireID); } - _ = circuit.fill(); + _ = try circuit.fill(); for (wires.slice()) |*wire| { const begin = wire.begin(); const end = wire.end(); @@ -534,13 +536,13 @@ fn updateCircuit() void { (circuit.isEnabled(cellEnd) and end.pinned)) wire.enabled = true; } map.reset(&assets.solid); - const enabledDoors = circuit.enabledDoors(); + const enabledDoors = try circuit.enabledDoors(); for (enabledDoors.constSlice()) |door| { - map.set_cell(door, 0); + try map.set_cell(door, 0); } } -fn wirePhysicsProcess(dt: f32, wire: *Wire) void { +fn wirePhysicsProcess(dt: f32, wire: *Wire) !void { var nodes = wire.nodes.slice(); if (nodes.len == 0) return; if (!inView(wire.begin().pos) and !inView(wire.end().pos)) return; @@ -550,7 +552,7 @@ fn wirePhysicsProcess(dt: f32, wire: *Wire) void { for (nodes) |*node| { velocityProcess(dt, node); physicsProcess(dt, node, &physics); - kinematicProcess(dt, node, &kinematic); + try kinematicProcess(dt, node, &kinematic); } var iterations: usize = 0; @@ -559,8 +561,8 @@ fn wirePhysicsProcess(dt: f32, wire: *Wire) void { while (left < nodes.len) : (left += 1) { // Left side constrainNodes(&nodes[left - 1], &nodes[left]); - kinematicProcess(dt, &nodes[left - 1], &kinematic); - kinematicProcess(dt, &nodes[left], &kinematic); + try kinematicProcess(dt, &nodes[left - 1], &kinematic); + try kinematicProcess(dt, &nodes[left], &kinematic); } } } @@ -678,10 +680,10 @@ fn controlProcess(_: f32, pos: *Pos, control: *Control, physics: *Physics, kinem pos.pos += move; } -fn kinematicProcess(_: f32, pos: *Pos, kinematic: *Kinematic) void { +fn kinematicProcess(_: f32, pos: *Pos, kinematic: *Kinematic) !void { var next = pos.last; next[0] = pos.pos[0]; - var hcol = map.collide(kinematic.col.addv(next)); + var hcol = try map.collide(kinematic.col.addv(next)); if (hcol.len > 0) { kinematic.lastCol[0] = next[0] - pos.last[0]; next[0] = pos.last[0]; @@ -690,7 +692,7 @@ fn kinematicProcess(_: f32, pos: *Pos, kinematic: *Kinematic) void { } next[1] = pos.pos[1]; - var vcol = map.collide(kinematic.col.addv(next)); + var vcol = try map.collide(kinematic.col.addv(next)); if (vcol.len > 0) { kinematic.lastCol[1] = next[1] - pos.last[1]; next[1] = pos.last[1]; @@ -699,7 +701,7 @@ fn kinematicProcess(_: f32, pos: *Pos, kinematic: *Kinematic) void { } var colPosAbs = next + kinematic.lastCol; - var lastCol = map.collide(kinematic.col.addv(colPosAbs)); + var lastCol = try map.collide(kinematic.col.addv(colPosAbs)); if (lastCol.len == 0) { kinematic.lastCol = Vec2f{ 0, 0 }; } diff --git a/src/main.zig b/src/main.zig index 8ff0ae3..93de7e3 100644 --- a/src/main.zig +++ b/src/main.zig @@ -27,13 +27,21 @@ export fn start() void { export fn update() void { const newState = switch (state) { .Menu => menu.update(), - .Game => game.update(time), + .Game => game.update(time) catch |e| switch (e) { + error.Overflow => showErr(@errorName(e)), + error.OutOfBounds => showErr(@errorName(e)), + error.IndexOutOfBounds => showErr(@errorName(e)), + }, }; if (state != newState) { state = newState; switch (newState) { .Menu => menu.start(), - .Game => game.start(), + .Game => game.start() catch |e| switch (e) { + error.Overflow => showErr(@errorName(e)), + error.OutOfBounds => showErr(@errorName(e)), + error.IndexOutOfBounds => showErr(@errorName(e)), + }, } } input.update(); diff --git a/src/map.zig b/src/map.zig index 610e2df..1076127 100644 --- a/src/map.zig +++ b/src/map.zig @@ -59,10 +59,10 @@ pub fn load_diff(this: *@This(), diff: []const u8) void { } } -pub fn set_cell(this: *@This(), cell: Cell, tile: u8) void { +pub fn set_cell(this: *@This(), cell: Cell, tile: u8) !void { const x = cell[0]; const y = cell[1]; - if (x < 0 or x > this.map_size[0] or y < 0 or y > this.map_size[1]) unreachable; + if (x < 0 or x > this.map_size[0] or y < 0 or y > this.map_size[1]) return error.OutOfBounds; const i = x + y * this.map_size[0]; this.tiles[@intCast(usize, i)] = tile; } @@ -109,23 +109,23 @@ fn getTile(this: @This(), x: i32, y: i32) ?u8 { return this.tiles[@intCast(u32, i)]; } -pub fn collide(this: @This(), rect: util.AABB) std.BoundedArray(util.AABB, 9) { +pub fn collide(this: @This(), rect: util.AABB) !std.BoundedArray(util.AABB, 9) { const top_left = rect.pos / tile_sizef; const bot_right = (rect.pos + rect.size) / tile_sizef; - var collisions = std.BoundedArray(util.AABB, 9).init(0) catch unreachable; + var collisions = try std.BoundedArray(util.AABB, 9).init(0); var i: isize = @floatToInt(i32, top_left[0]); while (i <= @floatToInt(i32, bot_right[0])) : (i += 1) { var a: isize = @floatToInt(i32, top_left[1]); while (a <= @floatToInt(i32, bot_right[1])) : (a += 1) { if (this.isSolid(Cell{ i, a })) { - collisions.append(util.AABB{ + try collisions.append(util.AABB{ .pos = Vec2f{ @intToFloat(f32, i * tile_width), @intToFloat(f32, a * tile_height), }, .size = tile_sizef, - }) catch unreachable; + }); } } } diff --git a/src/music.zig b/src/music.zig index f732f8d..83843f4 100644 --- a/src/music.zig +++ b/src/music.zig @@ -91,8 +91,8 @@ 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 cmd = MusicCommand.init(0) catch unreachable; + pub fn getNext(this: *@This(), dt: u32) !MusicCommand { + var cmd = try MusicCommand.init(0); const beatProgress = this.tick % this.beat; const beatTotal = @divTrunc(this.tick, this.beat); const beat = beatTotal % this.beatsPerBar; @@ -103,12 +103,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.append(Sfx{ + try cmd.append(Sfx{ .freq = .{ .start = this.nextNote(this.note) }, .duration = .{ .sustain = 5, .release = 5 }, .volume = 25, .flags = .{ .channel = .pulse2, .mode = .p25 }, - }) catch unreachable; + }); this.note += 1; } if (bar > collect.end) { @@ -116,24 +116,24 @@ pub const Procedural = struct { this.collect = null; } } - if (this.intensity.atLeast(.calm) and beat == 0 and beatProgress == 0) cmd.append(.{ + if (this.intensity.atLeast(.calm) and beat == 0 and beatProgress == 0) try cmd.append(.{ .freq = .{ .start = 220, .end = 110 }, .duration = .{ .release = 3 }, .volume = 100, .flags = .{ .channel = .triangle }, - }) catch unreachable; - if (this.intensity.atLeast(.active) and beat == this.beatsPerBar / 2 and beatProgress == 0) cmd.append(.{ + }); + if (this.intensity.atLeast(.active) and beat == this.beatsPerBar / 2 and beatProgress == 0) try cmd.append(.{ .freq = .{ .start = 110, .end = 55 }, .duration = .{ .release = 3 }, .volume = 100, .flags = .{ .channel = .triangle }, - }) catch unreachable; - if (this.walking and beat % 3 == 1 and beatProgress == 7) cmd.append(.{ + }); + if (this.walking and beat % 3 == 1 and beatProgress == 7) try cmd.append(.{ .freq = .{ .start = 1761, .end = 1 }, .duration = .{ .release = 5 }, .volume = 25, .flags = .{ .channel = .noise }, - }) catch unreachable; + }); return cmd; } }; diff --git a/src/util.zig b/src/util.zig index deaeb5f..e2dc763 100644 --- a/src/util.zig +++ b/src/util.zig @@ -67,13 +67,13 @@ pub const AABB = struct { pub fn Queue(comptime T: type, len: usize) type { return struct { data: std.BoundedArray(T, len), - pub fn init() @This() { + pub fn init() !@This() { return @This(){ - .data = std.BoundedArray(T, len).init(0) catch unreachable, + .data = try std.BoundedArray(T, len).init(0), }; } - pub fn insert(this: *@This(), t: T) void { - this.data.insert(0, t) catch unreachable; + pub fn insert(this: *@This(), t: T) !void { + try this.data.insert(0, t); } pub fn remove(this: *@This()) ?Cell { return this.data.popOrNull();