From 63ba55b56565e0d54e5c4b646f84f86097fd6ae6 Mon Sep 17 00:00:00 2001 From: Louis Pearson Date: Sun, 30 Jan 2022 02:15:55 -0700 Subject: [PATCH] Implment basic saves --- assets/tiles.png | Bin 717 -> 723 bytes src/main.zig | 224 +++++++++++++++++++++++++++++++++++++++++++++-- src/map.zig | 34 +++++-- 3 files changed, 244 insertions(+), 14 deletions(-) diff --git a/assets/tiles.png b/assets/tiles.png index e9b2b00a7c2db78a7c3c7016205fe87dd2f4e21e..1647dda224a2d0f3ad4183f5c1cd89940796a4bb 100644 GIT binary patch delta 645 zcmV;00($+;1=9tPS$}0oL_t(&f$dk>c7z}dP5A$h?kHsCf-s%74<$#9Ad+i#iG=tj zVn4bjs9x_m3jAHd?=zSCFaT{Wmls_FuB>gql`{Y~<>0*mfP3jRUGDQGUl25GB>LI{ zAevqP(9Y}o5dgA%0#^Ym^A1A*z#9O(tK2$q%n8(a%0Lu=dVk%T2@-%PAPZCp)XeY3 zBNIG0VRa|Zfz3Is(~~F<-U!1E0A&D_*CjGd8g-^f&*M`}rL1xNR24+r+)tR|{ED?*QN+ z)Eqp-a3`H<7XglCu*H=*#;`$g8(9q9B6YBECYaCb_$h=yM(k1pb(WQpz%&_ z)I?jo!~l>9)U`GNBp={3yXpg_w4>9AkAeWS7tl&@tAjcR5l{+4i7uOp0$uF&dB9~m zRe^sFq<4wvTL6GUrT~y(fC&V^z!bKXmKoPG_=L0s0Dm~)-1*#IvCMXT0e%Yr$YHnz zz<@|z&=FPvnA=TADxdskyM#m&1uOsMn_PN+M^BGfLE zYKY2g9C3}y8$hYNOaR!}C8Ct4{Pht4i{ysmK5{!?bw2^}x(2jl0!}6for(h>jGGCh z9g)0XwtivD7t}djrhyZ70EET9T!)jkd@;@+s^g#mF)qJm&jL0RPyo?!maZTaoSy

{ol@F>PAopks1A?V^{u;o<1W(0hM_kACpYX>4Jp2#L!tjF{ICSXHp+kob f9XfRA(0`^cM~Ddht45J700000NkvXXu0mjfpEn^l delta 639 zcmV-_0)YL~1U=UUU zl)YyF+Ov*<;-6CdJz}XB1L$+HeACmx#`*%-I0nF`JoqjFP^Uh_rQYB22ZD|bhQ5vf z3{7tU==Zb#2mtUpij#nyae+Yrzyko%RjN(|69Q=*ED!=fUVpb|0s}w`umVW}bn}aT zW`cziHg}RdusND}bOYGV2Vv*{fCT_{o*~vGlQV^RuiwU0${we!k|4nDeuJ@{uq%w` zZ2>6UFBt&Vg=&dt$hG$x0BRu_)TdoE^PZI&nRb7)o}D2_qOG85WY$I@QwaP704kyE z;3|flbcD>QLVu?VO%ZL8>V(KPp#ok6pmEqho10)8K*+~RsxqNVNZSAkK$!x{R}#H0 zn(Ji@ATj~D)*%4n187EfeV~+nIgQ9DP(ZzaUV?iaq_GeIm@pjC#Y0-4h&`VNM7ARp z$oGMCmxz7@AW+B@KxCM}1Oq@~3SNrJj5ssMgtP@9aDT$N^NF=|nRVxa_^SYb4Z|Y< z3}oa5>cuJu#ulMiy+A|b&<0UH1o;YUjealEw5%qf6Hs-@ca9BNx51t5J zXo`x;tnOip%L<@0?jhKmZxWHoQ}Ve4fJ1V}QLjjuYNI9(oK!<+0yHLweSmk0-2~$} zGV%i326#DNK<3p%Cr-#^n9H#ZPk$`mjPqqR3=beKC2t5gOh5{R;a2WAjn)WD@nmo8npbm`KiOPBsL ZeF0~m2uTW6%{Kr5002ovPDHLkV1g@q6c_*i diff --git a/src/main.zig b/src/main.zig index baca170..424a4fd 100644 --- a/src/main.zig +++ b/src/main.zig @@ -238,15 +238,6 @@ export fn start() void { .kinematic = .{ .col = .{ .pos = .{ -3, -6 }, .size = .{ 5, 5 } } }, }; - for (assets.coins) |coin| { - 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 unreachable; - } - for (assets.wire) |wire| { const begin = vec2tovec2f(wire.p1); const end = vec2tovec2f(wire.p2); @@ -273,6 +264,17 @@ export fn start() void { circuit.addDoor(door); } + if (!load()) { + for (assets.coins) |coin| { + 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 unreachable; + } + } + updateCircuit(); } @@ -304,6 +306,7 @@ export fn update() void { drawProcess(1, &player.pos, &player.sprite); { + var shouldSave = false; var remove = std.BoundedArray(usize, 10).init(0) catch unreachable; for (coins.slice()) |*coin, i| { staticAnimProcess(1, &coin.sprite, &coin.anim); @@ -312,11 +315,14 @@ export fn update() void { score += 1; remove.append(i) catch unreachable; music.playCollect(score); + shouldSave = true; } } while (remove.popOrNull()) |i| { _ = coins.swapRemove(i); } + // We save here to prevent duplicate coins + if (shouldSave) save(); } camera = @divTrunc(util.world2cell(player.pos.pos), @splat(2, @as(i32, 20))) * @splat(2, @as(i32, 20)); @@ -404,6 +410,206 @@ export fn update() void { time += 1; } +fn write_diff(writer: anytype, stride: usize, initial: []const u8, mapBuf: []const u8) !u8 { + var written: u8 = 0; + for (initial) |init_tile, i| { + if (mapBuf[i] != init_tile) { + const x = @intCast(u8, i % @intCast(usize, stride)); + const y = @intCast(u8, @divTrunc(i, @intCast(usize, stride))); + const temp = [3]u8{ x, y, mapBuf[i] }; + try writer.writeAll(&temp); + written += 1; + } + } + return written; +} + +pub fn load_diff(mapBuf: []u8, stride: usize, diff: []const u8) void { + var i: usize = 0; + while (i < diff.len) : (i += 3) { + const x = diff[i]; + const y = diff[i + 1]; + const tile = diff[i + 2]; + const a = x + y * stride; + mapBuf[a] = tile; + // this.set_cell(Cell{ x, y }, tile); + } +} + +fn load() bool { + var load_buf: [1024]u8 = undefined; + const read = w4.diskr(&load_buf, 1024); + w4.tracef("%d bytes read", read); + + for (load_buf[0 .. read - 1]) |byte| w4.tracef("%d", byte); + // if (true) return false; + if (read <= 0) return false; + + var stream = std.io.fixedBufferStream(load_buf[0..read]); + var reader = stream.reader(); + + var header: [5]u8 = undefined; + _ = reader.read(&header) catch w4.tracef("couldn't load header"); + w4.tracef("%s", &header); + if (!std.mem.eql(u8, "wired", &header)) return false; // w4.tracef("did not load, incorrect header bytes"); + + score = reader.readByte() catch return false; + + const obj_len = reader.readByte() catch return false; + const map_len = reader.readByte() catch return false; + const conduit_len = reader.readByte() catch return false; + + var i: usize = 0; + while (i < obj_len) : (i += 1) { + const b = reader.readByte() catch return false; + const obj = @intToEnum(SaveObj, @truncate(u4, b)); + const id = @truncate(u4, b >> 4); + const x = reader.readByte() catch return false; + const y = reader.readByte() catch return false; + const cell = util.Cell{ x, y }; + var pos = Pos.init(util.vec2ToVec2f(cell * Map.tile_size)); + if (obj == .WireBeginPinned or obj == .WireBeginLoose or obj == .WireEndPinned or obj == .WireEndLoose) + pos.pos += Vec2f{ 4, 4 }; + switch (obj) { + .Player => { + w4.tracef("player at %d, %d", x, y); + player.pos = pos; + // player.pos.pos += Vec2f{ 4, 6 }; + }, + .Coin => { + coins.append(.{ + .pos = pos, + .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 unreachable; + }, + .WireBeginPinned => { + var begin = wires.slice()[id].begin(); + begin.* = pos; + begin.pinned = true; + }, + .WireBeginLoose => { + var begin = wires.slice()[id].begin(); + begin.* = pos; + begin.pinned = false; + }, + .WireEndPinned => { + var end = wires.slice()[id].end(); + end.* = pos; + end.pinned = true; + }, + .WireEndLoose => { + var end = wires.slice()[id].end(); + end.* = pos; + end.pinned = false; + }, + } + } + + // Load map + var buf: [256]u8 = undefined; + // const len = reader.readByte() catch return; + const bytes_map = reader.read(buf[0 .. map_len * 3]) catch return false; + w4.tracef("loading %d map diffs... %d bytes", map_len, bytes_map); + load_diff(&solids_mutable, assets.solid_size[0], buf[0..bytes_map]); + + // Load conduit + // const conduit_len = reader.readByte() catch return; + const bytes_conduit = reader.read(buf[0 .. conduit_len * 3]) catch return false; + w4.tracef("loading %d conduit diffs... %d bytes", conduit_len, bytes_conduit); + for (buf[0..bytes_conduit]) |byte| w4.tracef("%d", byte); + load_diff(&conduit_mutable, assets.conduit_size[0], buf[0..bytes_conduit]); + + return true; +} + +const SaveObj = enum(u4) { + Player, + Coin, + WireBeginPinned, + WireBeginLoose, + WireEndPinned, + WireEndLoose, +}; + +fn cell2u8(cell: util.Cell) [2]u8 { + return [_]u8{ @intCast(u8, cell[0]), @intCast(u8, cell[1]) }; +} + +fn save() void { + var save_buf: [1024]u8 = undefined; + var save_stream = std.io.fixedBufferStream(&save_buf); + var save_writer = save_stream.writer(); + save_writer.writeAll("wired") catch return w4.tracef("Couldn't write header"); + save_writer.writeByte(score) catch return w4.tracef("Couldn't save score"); + w4.tracef("score %d written", score); + + // Write temporary length values + const lengths_start = save_stream.getPos() catch return w4.tracef("Couldn't get pos"); + save_writer.writeByte(0) catch return w4.tracef("Couldn't write obj length"); + save_writer.writeByte(0) catch return w4.tracef("Couldn't write map length"); + save_writer.writeByte(0) catch return w4.tracef("Couldn't write conduit length"); + + // Write player + const playerCell = util.world2cell(player.pos.pos); + save_writer.writeAll(&[_]u8{ @enumToInt(SaveObj.Player), @intCast(u8, playerCell[0]), @intCast(u8, playerCell[1]) }) catch return w4.tracef("Couldn't save player"); + var obj_len: u8 = 1; + + for (coins.slice()) |coin, i| { + obj_len += 1; + const id = @intCast(u8, @truncate(u4, i)) << 4; + const cell = util.world2cell(coin.pos.pos); + save_writer.writeByte(@enumToInt(SaveObj.Coin) | id) catch return w4.tracef("Couldn't save coin"); + save_writer.writeAll(&cell2u8(cell)) catch return; + } + + // Write wires + for (wires.slice()) |*wire, i| { + const id = @intCast(u8, @truncate(u4, i)) << 4; + const begin = wire.begin(); + const end = wire.end(); + obj_len += 1; + if (begin.pinned) { + const cell = util.world2cell(begin.pos); + save_writer.writeByte(@enumToInt(SaveObj.WireBeginPinned) | id) catch return w4.tracef("Couldn't save wire"); + save_writer.writeAll(&cell2u8(cell)) catch return; + } else { + const cell = util.world2cell(begin.pos); + save_writer.writeByte(@enumToInt(SaveObj.WireBeginLoose) | id) catch return w4.tracef("Couldn't save wire"); + save_writer.writeAll(&cell2u8(cell)) catch return; + } + obj_len += 1; + if (end.pinned) { + const cell = util.world2cell(end.pos); + save_writer.writeByte(@enumToInt(SaveObj.WireEndPinned) | id) catch return w4.tracef("Couldn't save wire"); + save_writer.writeAll(&cell2u8(cell)) catch return; + } else { + const cell = util.world2cell(end.pos); + save_writer.writeByte(@enumToInt(SaveObj.WireEndLoose) | id) catch return w4.tracef("Couldn't save wire"); + save_writer.writeAll(&cell2u8(cell)) catch return; + } + } + + // Write map + const map_len = write_diff(save_writer, assets.solid_size[0], &assets.solid, &solids_mutable) catch return w4.tracef("Couldn't save map diff"); + + // Write conduit + const conduit_len = write_diff(save_writer, assets.conduit_size[0], &assets.conduit, &conduit_mutable) catch return w4.tracef("Couldn't save map diff"); + + const endPos = save_stream.getPos() catch return; + save_stream.seekTo(lengths_start) catch w4.tracef("Couldn't seek"); + save_writer.writeByte(obj_len) catch return w4.tracef("Couldn't write obj length"); + save_writer.writeByte(map_len) catch return w4.tracef("Couldn't write map length"); + save_writer.writeByte(conduit_len) catch return w4.tracef("Couldn't write conduit length"); + + save_stream.seekTo(endPos) catch return; + const save_slice = save_stream.getWritten(); + const written = w4.diskw(save_slice.ptr, save_slice.len); + w4.tracef("%d bytes written, %d map diffs", written, map_len); + for (save_buf[0..written]) |byte| w4.tracef("%d", byte); +} + const Interaction = struct { pos: Vec2, details: union(enum) { diff --git a/src/map.zig b/src/map.zig index f66ffdf..610e2df 100644 --- a/src/map.zig +++ b/src/map.zig @@ -30,6 +30,35 @@ pub fn init(map: []u8, map_size: Vec2) @This() { return this; } +pub fn reset(this: *@This(), initialState: []const u8) void { + std.debug.assert(initialState.len == this.tiles.len); + std.mem.copy(u8, this.tiles, initialState); +} + +pub fn write_diff(this: *@This(), initialState: []const u8, buf: anytype) !u8 { + var written: u8 = 0; + for (initialState) |init_tile, i| { + if (this.tiles[i] != init_tile) { + const x = @intCast(u8, i % @intCast(usize, this.map_size[0])); + const y = @intCast(u8, @divTrunc(i, @intCast(usize, this.map_size[0]))); + const temp = [3]u8{ x, y, this.tiles[i] }; + try buf.writeAll(&temp); + written += 1; + } + } + return written; +} + +pub fn load_diff(this: *@This(), diff: []const u8) void { + var i: usize = 0; + while (i < diff.len) : (i += 3) { + const x = diff[i]; + const y = diff[i + 1]; + const tile = diff[i + 2]; + this.set_cell(Cell{ x, y }, tile); + } +} + pub fn set_cell(this: *@This(), cell: Cell, tile: u8) void { const x = cell[0]; const y = cell[1]; @@ -38,11 +67,6 @@ pub fn set_cell(this: *@This(), cell: Cell, tile: u8) void { this.tiles[@intCast(usize, i)] = tile; } -pub fn reset(this: *@This(), initialState: []const u8) void { - std.debug.assert(initialState.len == this.tiles.len); - std.mem.copy(u8, this.tiles, initialState); -} - pub fn get_cell(this: @This(), cell: Cell) ?u8 { const x = cell[0]; const y = cell[1];