Implment basic saves

master
Louis Pearson 2022-01-30 02:15:55 -07:00
parent 746d2c3a2f
commit 63ba55b565
3 changed files with 244 additions and 14 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 717 B

After

Width:  |  Height:  |  Size: 723 B

View File

@ -238,15 +238,6 @@ export fn start() void {
.kinematic = .{ .col = .{ .pos = .{ -3, -6 }, .size = .{ 5, 5 } } }, .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| { for (assets.wire) |wire| {
const begin = vec2tovec2f(wire.p1); const begin = vec2tovec2f(wire.p1);
const end = vec2tovec2f(wire.p2); const end = vec2tovec2f(wire.p2);
@ -273,6 +264,17 @@ export fn start() void {
circuit.addDoor(door); 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(); updateCircuit();
} }
@ -304,6 +306,7 @@ export fn update() void {
drawProcess(1, &player.pos, &player.sprite); drawProcess(1, &player.pos, &player.sprite);
{ {
var shouldSave = false;
var remove = std.BoundedArray(usize, 10).init(0) catch unreachable; var remove = std.BoundedArray(usize, 10).init(0) catch unreachable;
for (coins.slice()) |*coin, i| { for (coins.slice()) |*coin, i| {
staticAnimProcess(1, &coin.sprite, &coin.anim); staticAnimProcess(1, &coin.sprite, &coin.anim);
@ -312,11 +315,14 @@ export fn update() void {
score += 1; score += 1;
remove.append(i) catch unreachable; remove.append(i) catch unreachable;
music.playCollect(score); music.playCollect(score);
shouldSave = true;
} }
} }
while (remove.popOrNull()) |i| { while (remove.popOrNull()) |i| {
_ = coins.swapRemove(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)); 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; 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 { const Interaction = struct {
pos: Vec2, pos: Vec2,
details: union(enum) { details: union(enum) {

View File

@ -30,6 +30,35 @@ pub fn init(map: []u8, map_size: Vec2) @This() {
return 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 { pub fn set_cell(this: *@This(), cell: Cell, tile: u8) void {
const x = cell[0]; const x = cell[0];
const y = cell[1]; 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; 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 { pub fn get_cell(this: @This(), cell: Cell) ?u8 {
const x = cell[0]; const x = cell[0];
const y = cell[1]; const y = cell[1];