changeset 330:e0657709fbe3

rpg: add support for basic quests
author David Demelier <markand@malikania.fr>
date Wed, 06 Oct 2021 13:54:16 +0200
parents ea4a3af71c18
children 5c3f2aa95343
files src/libmlk-rpg/CMakeLists.txt src/libmlk-rpg/assets/sql/init.sql src/libmlk-rpg/assets/sql/quest-remove.sql src/libmlk-rpg/assets/sql/quest-save.sql src/libmlk-rpg/assets/sql/quest-step-load.sql src/libmlk-rpg/assets/sql/quest-step-save.sql src/libmlk-rpg/rpg/quest.c src/libmlk-rpg/rpg/quest.h src/libmlk-rpg/rpg/save.c src/libmlk-rpg/rpg/save.h src/libmlk-rpg/rpg/team.c tests/CMakeLists.txt tests/test-save-quest.c
diffstat 13 files changed, 381 insertions(+), 3 deletions(-) [+]
line wrap: on
line diff
--- a/src/libmlk-rpg/CMakeLists.txt	Mon Oct 04 13:30:09 2021 +0200
+++ b/src/libmlk-rpg/CMakeLists.txt	Wed Oct 06 13:54:16 2021 +0200
@@ -65,6 +65,8 @@
 	${libmlk-rpg_SOURCE_DIR}/rpg/message.h
 	${libmlk-rpg_SOURCE_DIR}/rpg/property.c
 	${libmlk-rpg_SOURCE_DIR}/rpg/property.h
+	${libmlk-rpg_SOURCE_DIR}/rpg/quest.c
+	${libmlk-rpg_SOURCE_DIR}/rpg/quest.h
 	${libmlk-rpg_SOURCE_DIR}/rpg/rpg.c
 	${libmlk-rpg_SOURCE_DIR}/rpg/rpg.h
 	${libmlk-rpg_SOURCE_DIR}/rpg/rpg_p.h
@@ -92,6 +94,10 @@
 	${libmlk-rpg_SOURCE_DIR}/assets/sql/property-load.sql
 	${libmlk-rpg_SOURCE_DIR}/assets/sql/property-remove.sql
 	${libmlk-rpg_SOURCE_DIR}/assets/sql/property-save.sql
+	${libmlk-rpg_SOURCE_DIR}/assets/sql/quest-remove.sql
+	${libmlk-rpg_SOURCE_DIR}/assets/sql/quest-save.sql
+	${libmlk-rpg_SOURCE_DIR}/assets/sql/quest-step-load.sql
+	${libmlk-rpg_SOURCE_DIR}/assets/sql/quest-step-save.sql
 )
 
 set(
--- a/src/libmlk-rpg/assets/sql/init.sql	Mon Oct 04 13:30:09 2021 +0200
+++ b/src/libmlk-rpg/assets/sql/init.sql	Wed Oct 06 13:54:16 2021 +0200
@@ -38,6 +38,17 @@
 	bonus_luck      INTEGER DEFAULT 0
 );
 
+CREATE TABLE IF NOT EXISTS quest(
+	name            TEXT PRIMARY KEY
+);
+
+CREATE TABLE IF NOT EXISTS quest_step(
+	name            TEXT PRIMARY KEY,
+	percent         INTEGER DEFAULT 0,
+	quest_name      TEXT NOT NULL,
+	FOREIGN KEY(quest_name) REFERENCES quest(name)
+);
+
 INSERT OR IGNORE INTO property(key, value) VALUES ('molko.create-date', strftime('%s','now'));
 INSERT OR IGNORE INTO property(key, value) VALUES ('molko.update-date', strftime('%s','now'));
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/libmlk-rpg/assets/sql/quest-remove.sql	Wed Oct 06 13:54:16 2021 +0200
@@ -0,0 +1,21 @@
+--
+-- quest-remove.sql -- remove a quest entirely
+--
+-- Copyright (c) 2020-2021 David Demelier <markand@malikania.fr>
+--
+-- Permission to use, copy, modify, and/or distribute this software for any
+-- purpose with or without fee is hereby granted, provided that the above
+-- copyright notice and this permission notice appear in all copies.
+--
+-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+-- WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+-- MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+-- ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+-- WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+-- ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+-- OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+--
+
+DELETE
+  FROM quest
+ WHERE name = ?
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/libmlk-rpg/assets/sql/quest-save.sql	Wed Oct 06 13:54:16 2021 +0200
@@ -0,0 +1,24 @@
+--
+-- quest-save.sql -- create parent quest entry
+--
+-- Copyright (c) 2020-2021 David Demelier <markand@malikania.fr>
+--
+-- Permission to use, copy, modify, and/or distribute this software for any
+-- purpose with or without fee is hereby granted, provided that the above
+-- copyright notice and this permission notice appear in all copies.
+--
+-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+-- WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+-- MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+-- ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+-- WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+-- ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+-- OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+--
+
+INSERT INTO quest(
+	name
+)
+VALUES(
+	?
+)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/libmlk-rpg/assets/sql/quest-step-load.sql	Wed Oct 06 13:54:16 2021 +0200
@@ -0,0 +1,21 @@
+--
+-- quest-step-load.sql -- remove a quest entirely
+--
+-- Copyright (c) 2020-2021 David Demelier <markand@malikania.fr>
+--
+-- Permission to use, copy, modify, and/or distribute this software for any
+-- purpose with or without fee is hereby granted, provided that the above
+-- copyright notice and this permission notice appear in all copies.
+--
+-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+-- WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+-- MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+-- ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+-- WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+-- ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+-- OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+--
+
+SELECT percent
+  FROM quest_step
+ WHERE name = ?
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/libmlk-rpg/assets/sql/quest-step-save.sql	Wed Oct 06 13:54:16 2021 +0200
@@ -0,0 +1,27 @@
+--
+-- quest-step-save.sql -- save a quest step
+--
+-- Copyright (c) 2020-2021 David Demelier <markand@malikania.fr>
+--
+-- Permission to use, copy, modify, and/or distribute this software for any
+-- purpose with or without fee is hereby granted, provided that the above
+-- copyright notice and this permission notice appear in all copies.
+--
+-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+-- WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+-- MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+-- ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+-- WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+-- ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+-- OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+--
+
+INSERT INTO quest_step(
+	quest_name,
+	name,
+	percent
+) VALUES(
+	?,
+	?,
+	?
+)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/libmlk-rpg/rpg/quest.c	Wed Oct 06 13:54:16 2021 +0200
@@ -0,0 +1,83 @@
+/*
+ * quest.c -- in game quests
+ *
+ * Copyright (c) 2020-2021 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <assert.h>
+
+#include <assets/sql/quest-remove.h>
+#include <assets/sql/quest-save.h>
+#include <assets/sql/quest-step-load.h>
+#include <assets/sql/quest-step-save.h>
+
+#include "quest.h"
+#include "save.h"
+
+int
+quest_save(struct quest *q, struct save *s)
+{
+	assert(q);
+	assert(s);
+
+	const struct quest_step *step;
+
+	if (save_tx_begin(s) < 0)
+		return -1;
+
+	if (save_exec(s, (const char *)assets_sql_quest_save, "s", q->name) < 0) {
+		save_tx_rollback(s);
+		return -1;
+	}
+
+	for (size_t i = 0; i < q->stepsz; ++i) {
+		step = &q->steps[i];
+		
+		if (save_exec(s, (const char *)assets_sql_quest_step_save, "ssi", q->name, step->name, step->percent) < 0) {
+			save_tx_rollback(s);
+			return -1;
+		}
+	}
+
+	save_tx_commit(s);
+
+	return 0;
+}
+
+int
+quest_load(struct quest *q, struct save *s)
+{
+	assert(q);
+	assert(s);
+
+	struct save_stmt stmt;
+	struct quest_step *step;
+
+	for (size_t i = 0; i < q->stepsz; ++i) {
+		step = &q->steps[i];
+		
+		if (save_stmt_init(&stmt, s, (const char *)assets_sql_quest_step_load, "s", step->name))
+			return -1;
+			
+		if (save_stmt_next(&stmt, "i", &step->percent) < 0) {
+			save_stmt_finish(&stmt);
+			return -1;
+		}
+		
+		save_stmt_finish(&stmt);
+	}
+
+	return 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/libmlk-rpg/rpg/quest.h	Wed Oct 06 13:54:16 2021 +0200
@@ -0,0 +1,51 @@
+/*
+ * quest.h -- in game quests
+ *
+ * Copyright (c) 2020-2021 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef MOLKO_RPG_QUEST_H
+#define MOLKO_RPG_QUEST_H
+
+#include <stddef.h>
+
+#include <core/core.h>
+
+struct save;
+
+struct quest_step {
+	const char *name;
+	const char *description;
+	int percent;
+};
+
+struct quest {
+	const char *name;
+	const char *description;
+	struct quest_step *steps;
+	size_t stepsz;
+};
+
+CORE_BEGIN_DECLS
+
+int
+quest_save(struct quest *, struct save *);
+
+int
+quest_load(struct quest *, struct save *);
+
+CORE_END_DECLS
+
+#endif /* !MOLKO_RPG_QUEST_H */
--- a/src/libmlk-rpg/rpg/save.c	Mon Oct 04 13:30:09 2021 +0200
+++ b/src/libmlk-rpg/rpg/save.c	Wed Oct 06 13:54:16 2021 +0200
@@ -210,10 +210,10 @@
 save_exec(struct save *db, const char *sql, const char *args, ...)
 {
 	assert(save_ok(db));
-	assert(sql && args);
+	assert(sql);
 
 	struct save_stmt stmt;
-	int ret;
+	enum save_stmt_errno ret;
 	va_list ap;
 
 	va_start(ap, args);
@@ -226,7 +226,7 @@
 	ret = save_stmt_next(&stmt, NULL);
 	save_stmt_finish(&stmt);
 
-	return ret;
+	return ret == SAVE_STMT_ERROR ? -1 : 0;
 }
 
 void
@@ -278,6 +278,7 @@
 		ret = SAVE_STMT_DONE;
 		break;
 	default:
+		errorf("%s", sqlite3_errmsg(stmt->parent->handle));
 		break;
 	}
 
@@ -292,3 +293,27 @@
 	sqlite3_finalize(stmt->handle);
 	memset(stmt, 0, sizeof (*stmt));
 }
+
+int
+save_tx_begin(struct save *s)
+{
+	assert(save_ok(s));
+
+	return save_exec(s, "BEGIN EXCLUSIVE TRANSACTION", NULL);
+}
+
+void
+save_tx_rollback(struct save *s)
+{
+	assert(save_ok(s));
+
+	(void)save_exec(s, "ROLLBACK", NULL);
+}
+
+void
+save_tx_commit(struct save *s)
+{
+	assert(save_ok(s));
+
+	(void)save_exec(s, "COMMIT", NULL);
+}
--- a/src/libmlk-rpg/rpg/save.h	Mon Oct 04 13:30:09 2021 +0200
+++ b/src/libmlk-rpg/rpg/save.h	Wed Oct 06 13:54:16 2021 +0200
@@ -72,6 +72,16 @@
 void
 save_stmt_finish(struct save_stmt *);
 
+/* Explicit transactions. */
+int
+save_tx_begin(struct save *);
+
+void
+save_tx_rollback(struct save *);
+
+void
+save_tx_commit(struct save *);
+
 CORE_END_DECLS
 
 #endif /* !MOLKO_RPG_SAVE_H */
--- a/src/libmlk-rpg/rpg/team.c	Mon Oct 04 13:30:09 2021 +0200
+++ b/src/libmlk-rpg/rpg/team.c	Wed Oct 06 13:54:16 2021 +0200
@@ -17,3 +17,4 @@
  */
 
 /* Nothing yet. */
+enum { some_compiler_needs_more_than_nothing = 1 };
--- a/tests/CMakeLists.txt	Mon Oct 04 13:30:09 2021 +0200
+++ b/tests/CMakeLists.txt	Wed Oct 06 13:54:16 2021 +0200
@@ -29,6 +29,7 @@
 	error
 	map
 	save
+	save-quest
 	state
 	tileset
 	util
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-save-quest.c	Wed Oct 06 13:54:16 2021 +0200
@@ -0,0 +1,97 @@
+/*
+ * test-save-quest.c -- test save routines
+ *
+ * Copyright (c) 2020-2021 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <stdio.h>
+
+#define GREATEST_USE_ABBREVS 0
+#include <greatest.h>
+
+#include <core/util.h>
+
+#include <rpg/quest.h>
+#include <rpg/save.h>
+
+static struct save db;
+
+static void
+clean(void *data)
+{
+	(void)data;
+
+	save_finish(&db);
+	remove("quest.db");
+}
+
+static void
+setup(void *data)
+{
+	(void)data;
+
+	if (save_open_path(&db, "quest.db", SAVE_MODE_WRITE) < 0)
+		exit(1);
+}
+
+GREATEST_TEST
+basics_load(void)
+{
+	struct quest_step steps[] = {
+		{
+			.name = "0001.01-kill-ten-moko",
+			.description = "Kill 10 mokos de las llanuras.",
+			.percent = 100
+		},
+		{
+			.name = "0001.02-bring-beer-to-molko",
+			.description = "Bring beer to Molko.",
+			.percent = 50
+		}
+	};
+	struct quest quest = {
+		.name = "0001-getting-started",
+		.description = "Initial quest for adventurer.",
+		.steps = steps,
+		.stepsz = UTIL_SIZE(steps)
+	};
+
+	GREATEST_ASSERT_EQ(quest_save(&quest, &db), 0);
+
+	/* Reset to inspect. */
+	steps[0].percent = steps[1].percent = 0;
+	GREATEST_ASSERT_EQ(quest_load(&quest, &db), 0);
+	GREATEST_ASSERT_EQ(steps[0].percent, 100);
+	GREATEST_ASSERT_EQ(steps[1].percent, 50);
+
+	GREATEST_PASS();
+}
+
+GREATEST_SUITE(suite_basics)
+{
+	GREATEST_SET_SETUP_CB(setup, NULL);
+	GREATEST_SET_TEARDOWN_CB(clean, NULL);
+	GREATEST_RUN_TEST(basics_load);
+}
+
+GREATEST_MAIN_DEFS();
+
+int
+main(int argc, char **argv)
+{
+	GREATEST_MAIN_BEGIN();
+	GREATEST_RUN_SUITE(suite_basics);
+	GREATEST_MAIN_END();
+}