summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Game.h5
-rw-r--r--src/GameInitDispose.cpp27
-rw-r--r--src/GameLoop.cpp1
-rw-r--r--src/GameTick.cpp17
-rw-r--r--src/main.zig9
-rw-r--r--src/misc.h13
-rw-r--r--src/misc.zig66
7 files changed, 90 insertions, 48 deletions
diff --git a/src/Game.h b/src/Game.h
index f6400d9..f10c136 100644
--- a/src/Game.h
+++ b/src/Game.h
@@ -25,6 +25,7 @@
 #include <GLFW/glfw3.h>
 
 #include "config.h"
+#include "misc.h"
 
 #ifdef __cplusplus
 #include <stdlib.h>
@@ -165,7 +166,6 @@ public:
 	// Game functions
 	void Tick();
 	void Splat(int k);
-	void saveHighScore();
 };
 #else // __cplusplus
 #include <stdbool.h>
@@ -176,7 +176,7 @@ typedef struct Game Game;
 #ifdef __cplusplus
 extern "C" {
 #endif // __cplusplus
-	Game* makeGame(struct Config config);
+	Game* makeGame(struct Config config, struct Scores scores);
 	void initGl(Game* game);
 	void initGame(Game* game);
 	void resizeWindow(Game* game, int width, int height);
@@ -185,6 +185,7 @@ extern "C" {
 	void handleKey(Game* game, int key, int action, int mods);
 	void setMenu(Game* game, bool value);
 	void eventLoop(Game* game);
+	struct Scores getScores(Game* game);
 	void closeGame(Game* game);
 #ifdef __cplusplus
 } // extern "C"
diff --git a/src/GameInitDispose.cpp b/src/GameInitDispose.cpp
index 9700e25..2928d5f 100644
--- a/src/GameInitDispose.cpp
+++ b/src/GameInitDispose.cpp
@@ -27,7 +27,6 @@
 #include <AL/alc.h>
 #include <GL/glu.h>
 
-#include "misc.h"
 #include "Game.h"
 #include "Support.h"
 
@@ -70,7 +69,7 @@ void resizeWindow(Game* game, int width, int height)
 	glViewport(0, 0, width, height);
 }
 
-Game* makeGame(Config config)
+Game* makeGame(Config config, Scores scores)
 {
 	auto game = new Game();
 	game->screenwidth = config.width;
@@ -93,20 +92,8 @@ Game* makeGame(Config config)
 	game->nummissions = config.levels.len;
 	game->debug = config.debug;
 
-	// TODO: Read high score
-	std::ifstream ipstream2 {"highscore.txt"};
-	if (!ipstream2) {
-		std::ofstream opstream("highscore.txt");
-		opstream << (game->highscore = 0) << std::endl;
-		opstream << (game->beatgame = 0) << std::endl;
-		opstream.close();
-	} else {
-		ipstream2 >> game->highscore;
-		ipstream2.ignore(256,'\n');
-		ipstream2 >> game->beatgame;
-		ipstream2.close();
-	}
-
+	game->highscore = scores.high_score;
+	game->beatgame = scores.completed;
 	game->disttest = true;
 	game->cubetest = true;
 	setMenu(game, true);
@@ -1092,6 +1079,14 @@ GLvoid Game::ReSizeGLScene(float fov, float near)
 	glLoadIdentity();
 }
 
+struct Scores getScores(Game* game)
+{
+	return {
+		.high_score = (size_t) game->highscore,
+		.completed = game->beatgame,
+	};
+}
+
 void closeGame(Game* game)
 {
 	const GLuint textures[] {
diff --git a/src/GameLoop.cpp b/src/GameLoop.cpp
index 2cd0c5c..06d4e90 100644
--- a/src/GameLoop.cpp
+++ b/src/GameLoop.cpp
@@ -19,7 +19,6 @@
 // You should have received a copy of the GNU General Public License
 // along with Black Shades.  If not, see <https://www.gnu.org/licenses/>.
 
-#include "misc.h"
 #include "Game.h"
 #include "Support.h"
 
diff --git a/src/GameTick.cpp b/src/GameTick.cpp
index fd9dad5..42af2a6 100644
--- a/src/GameTick.cpp
+++ b/src/GameTick.cpp
@@ -25,7 +25,6 @@
 #include <algorithm>
 #include <fstream>
 
-#include "misc.h"
 #include "Game.h"
 #include "Support.h"
 
@@ -89,19 +88,6 @@ void Game::Splat(int k)
 	alSourcePlay(gSourceID[headwhacksound]);
 }
 
-void Game::saveHighScore()
-{
-	if (score > highscore) {
-		highscore = score;
-		/* TODO */
-		std::ofstream opstream("highscore.txt");
-		opstream << highscore;
-		opstream << "\n";
-		opstream << beatgame;
-		opstream.close();
-	}
-}
-
 void Game::updateSong()
 {
 	if (environment == rainy_environment)
@@ -175,7 +161,6 @@ void click(Game* game, int button, int action, int mods)
 			}
 
 			alSourcePlay(gSourceID[losesound]);
-			game->saveHighScore();
 			break;
 		}
 		return;
@@ -457,6 +442,7 @@ void Game::Tick()
 
 	if (timeremaining <= 0) {
 		score += ++mission * 50 + 100;
+		highscore = max(score, highscore);
 
 		flashamount = flashg = 1;
 		flashr = flashb = 0;
@@ -477,7 +463,6 @@ void Game::Tick()
 
 			setMenu(this, beatgame = true);
 			gameinprogress = 0;
-			saveHighScore();
 		} else {
 			updateSong();
 			initGame(this);
diff --git a/src/main.zig b/src/main.zig
index 57ac609..4e93a8a 100644
--- a/src/main.zig
+++ b/src/main.zig
@@ -23,6 +23,7 @@ pub const c = @cImport({
 
 const Loca = @import("loca").Loca;
 const Window = gf.Window;
+const Scores = misc.Scores;
 const al = @import("zeal");
 const allocator = std.heap.c_allocator;
 const configuration = @import("config.zig");
@@ -30,6 +31,8 @@ const free = std.c.free;
 const gf = @import("gfz");
 const gl = @import("zgl");
 const misc = @import("misc.zig");
+const loadScores = misc.loadScores;
+const saveScores = misc.saveScores;
 const std = @import("std");
 
 var game: *c.Game = undefined;
@@ -63,7 +66,11 @@ pub fn main() !void {
                                      "Black Shades", .{}, .{});
     try window.makeCurrent();
 
-    game = c.makeGame(@bitCast(c.Config, config)).?;
+    game = c.makeGame(@bitCast(c.Config, config),
+                      @bitCast(c.Scores, try loadScores(loca.user_data))).?;
+    defer saveScores(loca.user_data, @bitCast(Scores, c.getScores(game)))
+        catch unreachable;
+
     try window.setSizeCallback(resizeWindow);
     try gf.swapInterval(@boolToInt(config.vsync));
     c.initGl(game);
diff --git a/src/misc.h b/src/misc.h
index fbd70cd..be5570a 100644
--- a/src/misc.h
+++ b/src/misc.h
@@ -47,13 +47,18 @@ struct MuscleData {
 	signed char parent2;
 };
 
+struct Scores {
+	size_t high_score;
+	bool completed;
+};
+
 #ifdef __cplusplus
 extern "C" {
 #endif // __cplusplus
-	AnimationData loadAnimation(const char*);
-	void loadJoints(JointData*);
-	ModelData loadModel(const char*);
-	void loadMuscles(MuscleData*);
+	struct AnimationData loadAnimation(const char*);
+	void loadJoints(struct JointData*);
+	struct ModelData loadModel(const char*);
+	void loadMuscles(struct MuscleData*);
 	ALuint loadSound(const char*);
 	GLuint loadTexture(const char*);
 	void playSound(ALuint source, ALfloat x, ALfloat y, ALfloat z);
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);
+}