aboutsummaryrefslogtreecommitdiff
path: root/src/misc.zig
diff options
context:
space:
mode:
authorNguyễn Gia Phong <mcsinyx@disroot.org>2021-10-20 23:19:17 +0700
committerNguyễn Gia Phong <mcsinyx@disroot.org>2021-10-20 23:19:17 +0700
commitbd321d051ca8c827a5c8a490afc33812e76abba7 (patch)
treede88e5b28345672d952c5cf988dd05b4339aaa74 /src/misc.zig
parent446f5c5fd7cd977f096cd2d6256d829a765467c9 (diff)
downloadblackshades-bd321d051ca8c827a5c8a490afc33812e76abba7.tar.gz
Move scores tracking to user data
Diffstat (limited to 'src/misc.zig')
-rw-r--r--src/misc.zig66
1 files changed, 58 insertions, 8 deletions
diff --git a/src/misc.zig b/src/misc.zig
index 2e54416..e30c154 100644
--- a/src/misc.zig
+++ b/src/misc.zig
@@ -35,7 +35,9 @@ const data_dir = @import("build_options").data_dir ++ [_]u8{ sep };
const endsWith = std.mem.endsWith;
const eql = std.mem.eql;
const free = std.c.free;
+const ini = @import("ini");
const join = std.fs.path.joinZ;
+const hash = std.crypto.hash.Blake3.hash;
const maxInt = std.math.maxInt;
const parseFloat = std.fmt.parseFloat;
const parseInt = std.fmt.parseInt;
@@ -45,14 +47,12 @@ const startsWith = std.mem.startsWith;
const std = @import("std");
const tokenize = std.mem.tokenize;
-// Don't judge me, take care of me!
-const max_size = maxInt(usize);
-
/// Read given file to heap, allocated by C allocator.
fn readFile(dir: Dir, comptime fmt: []const u8, args: anytype) ![]const u8 {
const filename = try allocPrint(allocator, fmt, args);
defer allocator.free(filename);
- return dir.readFileAlloc(allocator, filename, max_size);
+ // Don't judge me, take care of me!
+ return dir.readFileAlloc(allocator, filename, maxInt(usize));
}
const Frame = extern struct {
@@ -268,6 +268,36 @@ export fn loadMuscles(muscles: [*]Muscle) void {
muscles[i].load(row) catch unreachable;
}
+pub const Scores = extern struct {
+ // Don't underestimate players!
+ high_score: usize = 0,
+ completed: bool = false,
+};
+
+/// Read scores from user data directory and return it.
+pub fn loadScores(base_dir: []const u8) !Scores {
+ var dir = try cwd().openDir(base_dir, .{});
+ defer dir.close();
+ var scores = Scores{};
+ const path = "blackshades" ++ [_]u8{ sep } ++ "scores.ini";
+ const input = dir.openFile(path, .{}) catch return scores;
+ defer input.close();
+ var parser = ini.parse(allocator, input.reader());
+ defer parser.deinit();
+
+ while (try parser.next()) |record|
+ switch (record) {
+ .section => {},
+ .property => |kv| if (eql(u8, kv.key, "high score")) {
+ scores.high_score = try parseInt(usize, kv.value, 10);
+ } else if (eql(u8, kv.key, "completed")) {
+ scores.completed = try parseBool(kv.value);
+ } else return error.InvalidData,
+ else => return error.InvalidData,
+ };
+ return scores;
+}
+
/// Load audio file into an OpenAL buffer and return it.
export fn loadSound(filename: [*:0]const u8) u32 {
const path = join(allocator, &.{ data_dir ++ "sounds", span(filename) })
@@ -287,10 +317,9 @@ fn check(errorString: fn (c_uint) callconv(.C) [*c]const u8,
/// Load PNG file into an OpenGL buffer and return it.
export fn loadTexture(filename: [*:0]const u8) GLuint {
- var dir = cwd().openDir(data_dir ++ "textures", .{}) catch unreachable;
- defer dir.close();
- const file = dir.readFileAlloc(allocator, span(filename), max_size)
- catch unreachable;
+ const file = readFile(cwd(), data_dir ++ "textures{c}{s}", .{
+ sep, filename,
+ }) catch unreachable;
defer allocator.free(file);
var data: [*c]u8 = undefined;
@@ -325,3 +354,24 @@ export fn playSound(source: u32, x: f32, y: f32, z: f32) void {
src.setPosition(.{ x / 32, y / 32, z / 32 }) catch unreachable;
src.play() catch unreachable;
}
+
+// TODO: replace with @maximum from Zig 0.9+
+fn maximum(a: anytype, b: @TypeOf(a)) @TypeOf(a) {
+ return if (a > b) a else b;
+}
+
+/// Write scores to user data directory.
+pub fn saveScores(base_dir: []const u8, current: Scores) !void {
+ const previous = try loadScores(base_dir);
+ const dir_path = try join(allocator, &.{ base_dir, "blackshades" });
+ defer allocator.free(dir_path);
+ var dir = try cwd().openDir(dir_path, .{});
+ defer dir.close();
+ const format = "[scores]\nhigh score = {}\ncompleted = {}\n";
+ const data = try allocPrint(allocator, format, .{
+ maximum(previous.high_score, current.high_score),
+ previous.completed or current.completed,
+ });
+ defer allocator.free(data);
+ try dir.writeFile("scores.ini", data);
+}