# HG changeset patch # User David Demelier # Date 1633521256 -7200 # Node ID e0657709fbe3f3abf26c87938fa5b11ea577aad9 # Parent ea4a3af71c18808cba9c712f5e837391d183e1e8 rpg: add support for basic quests diff -r ea4a3af71c18 -r e0657709fbe3 src/libmlk-rpg/CMakeLists.txt --- 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( diff -r ea4a3af71c18 -r e0657709fbe3 src/libmlk-rpg/assets/sql/init.sql --- 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')); diff -r ea4a3af71c18 -r e0657709fbe3 src/libmlk-rpg/assets/sql/quest-remove.sql --- /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 +-- +-- 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 = ? diff -r ea4a3af71c18 -r e0657709fbe3 src/libmlk-rpg/assets/sql/quest-save.sql --- /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 +-- +-- 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( + ? +) diff -r ea4a3af71c18 -r e0657709fbe3 src/libmlk-rpg/assets/sql/quest-step-load.sql --- /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 +-- +-- 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 = ? diff -r ea4a3af71c18 -r e0657709fbe3 src/libmlk-rpg/assets/sql/quest-step-save.sql --- /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 +-- +-- 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( + ?, + ?, + ? +) diff -r ea4a3af71c18 -r e0657709fbe3 src/libmlk-rpg/rpg/quest.c --- /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 + * + * 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 + +#include +#include +#include +#include + +#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; +} diff -r ea4a3af71c18 -r e0657709fbe3 src/libmlk-rpg/rpg/quest.h --- /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 + * + * 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 + +#include + +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 */ diff -r ea4a3af71c18 -r e0657709fbe3 src/libmlk-rpg/rpg/save.c --- 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); +} diff -r ea4a3af71c18 -r e0657709fbe3 src/libmlk-rpg/rpg/save.h --- 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 */ diff -r ea4a3af71c18 -r e0657709fbe3 src/libmlk-rpg/rpg/team.c --- 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 }; diff -r ea4a3af71c18 -r e0657709fbe3 tests/CMakeLists.txt --- 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 diff -r ea4a3af71c18 -r e0657709fbe3 tests/test-save-quest.c --- /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 + * + * 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 + +#define GREATEST_USE_ABBREVS 0 +#include + +#include + +#include +#include + +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(); +}