Mercurial > molko
changeset 281:87b8c7510717
rpg: implement load/save for characters
line wrap: on
line diff
--- a/extern/libgreatest/CMakeLists.txt Tue Dec 15 22:07:18 2020 +0100 +++ b/extern/libgreatest/CMakeLists.txt Sun Dec 20 10:55:53 2020 +0100 @@ -22,6 +22,7 @@ TARGET libgreatest TYPE INTERFACE SOURCES greatest.h + FOLDER extern EXTERNAL INCLUDES INTERFACE $<BUILD_INTERFACE:${libgreatest_SOURCE_DIR}> )
--- a/libmlk-adventure/CMakeLists.txt Tue Dec 15 22:07:18 2020 +0100 +++ b/libmlk-adventure/CMakeLists.txt Sun Dec 20 10:55:53 2020 +0100 @@ -29,6 +29,8 @@ ${libadventure_SOURCE_DIR}/adventure/adventure_p.h ${libadventure_SOURCE_DIR}/adventure/assets.c ${libadventure_SOURCE_DIR}/adventure/assets.h + ${libadventure_SOURCE_DIR}/adventure/dialog/save.c + ${libadventure_SOURCE_DIR}/adventure/dialog/save.h ${libadventure_SOURCE_DIR}/adventure/mapscene/mapscene.c ${libadventure_SOURCE_DIR}/adventure/mapscene/mapscene.h ${libadventure_SOURCE_DIR}/adventure/molko.c @@ -41,6 +43,8 @@ ${libadventure_SOURCE_DIR}/adventure/state/panic.h ${libadventure_SOURCE_DIR}/adventure/state/splashscreen.c ${libadventure_SOURCE_DIR}/adventure/state/splashscreen.h + ${libadventure_SOURCE_DIR}/adventure/state/continue.c + ${libadventure_SOURCE_DIR}/adventure/state/continue.h ${libadventure_SOURCE_DIR}/adventure/trace_hud.c ${libadventure_SOURCE_DIR}/adventure/trace_hud.h )
--- a/libmlk-adventure/adventure/action/chest.c Tue Dec 15 22:07:18 2020 +0100 +++ b/libmlk-adventure/adventure/action/chest.c Sun Dec 20 10:55:53 2020 +0100 @@ -24,11 +24,11 @@ #include <core/event.h> #include <core/maths.h> #include <core/panic.h> -#include <core/save.h> #include <core/sound.h> #include <core/sprite.h> #include <rpg/map.h> +#include <rpg/save.h> #include "chest.h"
--- a/libmlk-adventure/adventure/assets.c Tue Dec 15 22:07:18 2020 +0100 +++ b/libmlk-adventure/adventure/assets.c Sun Dec 20 10:55:53 2020 +0100 @@ -37,7 +37,8 @@ struct sprite sprite; } table_sprites[] = { SPRITE(ASSETS_SPRITE_UI_CURSOR, "sprites/ui-cursor.png", 24, 24), - SPRITE(ASSETS_SPRITE_CHEST, "sprites/chest.png", 32, 32) + SPRITE(ASSETS_SPRITE_CHEST, "sprites/chest.png", 32, 32), + SPRITE(ASSETS_SPRITE_FACES, "sprites/faces.png", 144, 144) }; struct sprite *assets_sprites[ASSETS_SPRITE_NUM] = {0};
--- a/libmlk-adventure/adventure/assets.h Tue Dec 15 22:07:18 2020 +0100 +++ b/libmlk-adventure/adventure/assets.h Sun Dec 20 10:55:53 2020 +0100 @@ -28,6 +28,9 @@ /* Actions. */ ASSETS_SPRITE_CHEST, + /* Team assets. */ + ASSETS_SPRITE_FACES, + ASSETS_SPRITE_NUM };
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libmlk-adventure/adventure/dialog/save.c Sun Dec 20 10:55:53 2020 +0100 @@ -0,0 +1,302 @@ +/* + * save.c -- select a save slot + * + * Copyright (c) 2020 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 <stdio.h> +#include <string.h> + +#include <compat.h> + +#include <core/event.h> +#include <core/font.h> +#include <core/maths.h> +#include <core/painter.h> +#include <core/sprite.h> +#include <core/window.h> + +#include <ui/align.h> +#include <ui/label.h> +#include <ui/frame.h> +#include <ui/theme.h> + +#include <adventure/adventure_p.h> +#include <adventure/assets.h> + +#include "save.h" + +#define THEME(s) ((s)->theme ? (s)->theme : theme_default()) +#define LINES_MAX (3) + +/* TODO: require a module for this*/ +#define TEAM_MAX (4) + +struct geo { + int x, y; + unsigned int w, h; + + /* Per line height/padding. */ + unsigned int lh; + unsigned int lp; + + struct { + int x, y; + unsigned int w, h; + + /* Lines start positions. */ + int lx, ly; + + struct { + int x, y; + unsigned int w, h; + } faces[TEAM_MAX]; + } saves[DIALOG_SAVE_MAX]; +}; + +static struct geo +geometry(const struct dialog_save *dlg) +{ + struct geo geo = { + .w = window.w - THEME(dlg)->padding * 40, + .h = window.h - THEME(dlg)->padding * 10, + }; + + align(ALIGN_CENTER, &geo.x, &geo.y, geo.w, geo.h, 0, 0, window.w, window.h); + + for (size_t i = 0; i < DIALOG_SAVE_MAX; ++i) { + const unsigned int padding = THEME(dlg)->padding; + + geo.saves[i].w = (geo.w - padding * 2); + geo.saves[i].h = (geo.h - (padding * (DIALOG_SAVE_MAX + 1))) / DIALOG_SAVE_MAX; + geo.saves[i].x = (geo.x + padding); + geo.saves[i].y = (geo.y + padding) + i * (padding + geo.saves[i].h); + geo.saves[i].lx = (geo.saves[i].x + padding); + + /* Compute lines padding (we draw LINES_MAX lines). */ + geo.lh = font_height(THEME(dlg)->fonts[THEME_FONT_INTERFACE]); + geo.lp = (geo.saves[i].h - (geo.lh * LINES_MAX)) / (LINES_MAX + 1); + + /* Compute faces position. */ + for (size_t f = 0; f < TEAM_MAX; ++f) { + geo.saves[i].faces[f].h = geo.saves[i].h - padding * 2; + geo.saves[i].faces[f].w = geo.saves[i].faces[f].h; + geo.saves[i].faces[f].x = (geo.saves[i].x + padding) + (f * (padding + geo.saves[i].faces[f].w)); + geo.saves[i].faces[f].y = (geo.saves[i].y + padding); + geo.saves[i].lx += geo.saves[i].faces[f].w + padding; + } + + geo.saves[i].ly = geo.saves[i].y + geo.lp; + } + + return geo; +} + +static void +draw_frame(const struct geo *geo) +{ + struct frame f = { + .x = geo->x, + .y = geo->y, + .w = geo->w, + .h = geo->h, + }; + + frame_draw(&f); +} + +static void +draw_save_box(const struct geo *geo, size_t i) +{ + /* TODO: change colors at some point. */ + painter_set_color(0x884b2bff); + painter_draw_rectangle(geo->saves[i].x, geo->saves[i].y, geo->saves[i].w, geo->saves[i].h); +} + +static void +draw_save_faces(const struct dialog_save *dlg, const struct geo *geo, size_t i) +{ + /* TODO: determine face. */ + for (size_t f = 0; f < TEAM_MAX; ++f) { + sprite_scale(assets_sprites[ASSETS_SPRITE_FACES], 0, f, + geo->saves[i].faces[f].x, + geo->saves[i].faces[f].y, + geo->saves[i].faces[f].w, + geo->saves[i].faces[f].h + ); + } +} + +static void +draw_save_times(const struct dialog_save *dlg, const struct geo *geo, size_t i) +{ + struct label label = {0}; + char time[128], line[256]; + struct save_property prop; + + label.theme = dlg->theme; + label.x = geo->saves[i].lx; + label.y = geo->saves[i].ly; + label.flags = LABEL_FLAGS_SHADOW; + label.text = line; + + /* TODO: Get map position. */ + strlcpy(line, "World", sizeof (line)); + label_draw(&label); + + /* Last time. */ + strftime(time, sizeof (time), "%c", localtime(&dlg->saves[i].updated)); + snprintf(line, sizeof (line), _("Last played: %s"), time); + + label.y += geo->lp + geo->lh; + label_draw(&label); + + /* TODO: Time played. */ + snprintf(line, sizeof (line), _("Time played: %s"), "100 hours"); + + label.y += geo->lp + geo->lh; + label_draw(&label); +} + +static void +draw_save(const struct dialog_save *dlg, const struct geo *geo, size_t i) +{ + draw_save_box(geo, i); + + /* Do not draw the content if save is invalid. */ + if (!save_ok(&dlg->saves[i])) + return; + + draw_save_faces(dlg, geo, i); + draw_save_times(dlg, geo, i); +} + +static void +draw_saves(const struct dialog_save *dlg, const struct geo *geo) +{ + for (size_t i = 0; i < DIALOG_SAVE_MAX; ++i) + draw_save(dlg, geo, i); +} + +static void +draw_cursor(const struct dialog_save *dlg, const struct geo *geo) +{ + const struct sprite *sprite =assets_sprites[ASSETS_SPRITE_UI_CURSOR]; + const int x = geo->saves[dlg->selected].x - sprite->cellw; + const int y = geo->saves[dlg->selected].y; + + sprite_draw(sprite, 1, 2, x, y + (geo->saves[dlg->selected].h / 2) - (sprite->cellh / 2)); +} + +static bool +handle_keydown(struct dialog_save *s, const struct event_key *key) +{ + assert(key->type == EVENT_KEYDOWN); + + switch (key->key) { + case KEY_UP: + if (s->selected == 0) + s->selected = DIALOG_SAVE_MAX - 1; + else + s->selected --; + break; + case KEY_DOWN: + if (s->selected + 1 >= DIALOG_SAVE_MAX) + s->selected = 0; + else + s->selected ++; + break; + case KEY_ENTER: + return save_ok(&s->saves[s->selected]); + default: + break; + } + + return false; +} + +static bool +handle_clickdown(struct dialog_save *s, const struct geo *geo, const struct event_click *clk) +{ + assert(clk->type == EVENT_CLICKDOWN); + + for (size_t i = 0; i < DIALOG_SAVE_MAX; ++i) { + if (maths_is_boxed(geo->saves[i].x, geo->saves[i].y, + geo->saves[i].w, geo->saves[i].h, + clk->x, clk->y)) { + s->selected = i; + break; + } + } + + return clk->clicks >= 2 && save_ok(&s->saves[s->selected]); +} + +void +dialog_save_init(struct dialog_save *s) +{ + assert(s); + + for (size_t i = 0; i < DIALOG_SAVE_MAX; ++i) + save_open(&s->saves[i], i, SAVE_MODE_READ); +} + +bool +dialog_save_handle(struct dialog_save *dlg, const union event *ev) +{ + assert(dlg); + assert(ev); + + const struct geo geo = geometry(dlg); + + switch (ev->type) { + case EVENT_KEYDOWN: + return handle_keydown(dlg, &ev->key); + case EVENT_CLICKDOWN: + return handle_clickdown(dlg, &geo, &ev->click); + default: + break; + } + + return false; +} + +void +dialog_save_update(struct dialog_save *dlg, unsigned int ticks) +{ + assert(dlg); +} + +void +dialog_save_draw(const struct dialog_save *dlg) +{ + assert(dlg); + + const struct geo geo = geometry(dlg); + + draw_frame(&geo); + draw_saves(dlg, &geo); + draw_cursor(dlg, &geo); +} + +void +dialog_save_finish(struct dialog_save *dlg) +{ + assert(dlg); + + for (size_t i = 0; i < DIALOG_SAVE_MAX; ++i) + save_finish(&dlg->saves[i]); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libmlk-adventure/adventure/dialog/save.h Sun Dec 20 10:55:53 2020 +0100 @@ -0,0 +1,53 @@ +/* + * save.h -- select a save slot + * + * Copyright (c) 2020 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_ADVENTURE_SAVE_H +#define MOLKO_ADVENTURE_SAVE_H + +#include <stdbool.h> + +#include <rpg/save.h> + +#define DIALOG_SAVE_MAX (6) + +union event; + +struct theme; + +struct dialog_save { + const struct theme *theme; + struct save saves[DIALOG_SAVE_MAX]; + size_t selected; +}; + +void +dialog_save_init(struct dialog_save *); + +bool +dialog_save_handle(struct dialog_save *, const union event *); + +void +dialog_save_update(struct dialog_save *, unsigned int); + +void +dialog_save_draw(const struct dialog_save *); + +void +dialog_save_finish(struct dialog_save *); + +#endif /* !MOLKO_ADVENTURE_SAVE_H */
--- a/libmlk-adventure/adventure/molko.c Tue Dec 15 22:07:18 2020 +0100 +++ b/libmlk-adventure/adventure/molko.c Sun Dec 20 10:55:53 2020 +0100 @@ -86,7 +86,12 @@ assets_init(); /* Start to splash. */ +#if 0 + // TODO: put back this. game_switch(splashscreen_state_new(), true); +#else + game_switch(mainmenu_state_new(), true); +#endif } void
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libmlk-adventure/adventure/state/continue.c Sun Dec 20 10:55:53 2020 +0100 @@ -0,0 +1,109 @@ +/* + * continue.c -- select save to continue the game + * + * Copyright (c) 2020 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 <core/alloc.h> +#include <core/event.h> +#include <core/game.h> +#include <core/painter.h> +#include <core/state.h> + +#include <adventure/dialog/save.h> + +#include "mainmenu.h" +#include "continue.h" + +struct self { + struct state state; + struct dialog_save dialog; +}; + +static void +start(struct state *state) +{ + struct self *self = state->data; + + dialog_save_init(&self->dialog); +} + +static void +handle(struct state *state, const union event *ev) +{ + struct self *self = state->data; + bool selected = false; + + switch (ev->type) { + case EVENT_QUIT: + game_quit(); + break; + case EVENT_KEYDOWN: + if (ev->key.key == KEY_ESCAPE) + game_switch(mainmenu_state_new(), false); + else + selected = dialog_save_handle(&self->dialog, ev); + break; + default: + selected = dialog_save_handle(&self->dialog, ev); + break; + } + + if (selected) + game_quit(); +} + +static void +update(struct state *state, unsigned int ticks) +{ + struct self *self = state->data; + + dialog_save_update(&self->dialog, ticks); +} + +static void +draw(struct state *state) +{ + struct self *self = state->data; + + painter_set_color(0xffffffff); + painter_clear(); + dialog_save_draw(&self->dialog); + painter_present(); +} + +static void +finish(struct state *state) +{ + struct self *self = state->data; + + dialog_save_finish(&self->dialog); +} + +struct state * +continue_state_new(void) +{ + struct self *self; + + self = alloc_new0(sizeof (*self)); + self->state.data = self; + self->state.start = start; + self->state.handle = handle; + self->state.update = update; + self->state.draw = draw; + self->state.finish = finish; + + return &self->state; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libmlk-adventure/adventure/state/continue.h Sun Dec 20 10:55:53 2020 +0100 @@ -0,0 +1,27 @@ +/* + * continue.h -- select save to continue the game + * + * Copyright (c) 2020 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_ADVENTURE_CONTINUE_H +#define MOLKO_ADVENTURE_CONTINUE_H + +struct state; + +struct state * +continue_state_new(void); + +#endif /* !MOLKO_ADVENTURE_CONTINUE_H */
--- a/libmlk-adventure/adventure/state/mainmenu.c Tue Dec 15 22:07:18 2020 +0100 +++ b/libmlk-adventure/adventure/state/mainmenu.c Sun Dec 20 10:55:53 2020 +0100 @@ -41,6 +41,7 @@ #include <adventure/adventure_p.h> #include "mainmenu.h" +#include "continue.h" struct self { struct state state; @@ -63,7 +64,7 @@ static void resume(void) { - /* TODO: implement here. */ + game_switch(continue_state_new(), false); } static void
--- a/libmlk-core/CMakeLists.txt Tue Dec 15 22:07:18 2020 +0100 +++ b/libmlk-core/CMakeLists.txt Sun Dec 20 10:55:53 2020 +0100 @@ -21,14 +21,6 @@ include(CheckLibraryExists) set( - ASSETS - ${libmlk-core_SOURCE_DIR}/assets/sql/init.sql - ${libmlk-core_SOURCE_DIR}/assets/sql/property-get.sql - ${libmlk-core_SOURCE_DIR}/assets/sql/property-remove.sql - ${libmlk-core_SOURCE_DIR}/assets/sql/property-set.sql -) - -set( PO ${libmlk-core_SOURCE_DIR}/nls/fr.po ) @@ -70,8 +62,6 @@ ${libmlk-core_SOURCE_DIR}/core/painter.h ${libmlk-core_SOURCE_DIR}/core/panic.c ${libmlk-core_SOURCE_DIR}/core/panic.h - ${libmlk-core_SOURCE_DIR}/core/save.c - ${libmlk-core_SOURCE_DIR}/core/save.h ${libmlk-core_SOURCE_DIR}/core/script.c ${libmlk-core_SOURCE_DIR}/core/script.h ${libmlk-core_SOURCE_DIR}/core/sound.c @@ -112,7 +102,6 @@ molko_define_library( TARGET libmlk-core SOURCES ${SOURCES} ${ASSETS} ${PO} - ASSETS ${ASSETS} TRANSLATIONS fr LIBRARIES PUBLIC
--- a/libmlk-core/assets/sql/init.sql Tue Dec 15 22:07:18 2020 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,30 +0,0 @@ --- --- init.sql -- initialize database --- --- Copyright (c) 2020 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. --- - -BEGIN EXCLUSIVE TRANSACTION; - -CREATE TABLE IF NOT EXISTS property( - id INTEGER PRIMARY KEY AUTOINCREMENT, - key TEXT NOT NULL UNIQUE, - value TEXT NOT NULL -); - -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')); - -COMMIT;
--- a/libmlk-core/assets/sql/property-get.sql Tue Dec 15 22:07:18 2020 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,21 +0,0 @@ --- --- property-get.sql -- get a property --- --- Copyright (c) 2020 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 value - FROM property - WHERE key = ?
--- a/libmlk-core/assets/sql/property-remove.sql Tue Dec 15 22:07:18 2020 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,21 +0,0 @@ --- --- property-remove.sql -- remove a property --- --- Copyright (c) 2020 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 property - WHERE key = ?
--- a/libmlk-core/assets/sql/property-set.sql Tue Dec 15 22:07:18 2020 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,26 +0,0 @@ --- --- property-set.sql -- set a property --- --- Copyright (c) 2020 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 OR REPLACE INTO property( - key, - value -) -VALUES( - ?, - ? -)
--- a/libmlk-core/core/event.c Tue Dec 15 22:07:18 2020 +0100 +++ b/libmlk-core/core/event.c Sun Dec 20 10:55:53 2020 +0100 @@ -212,6 +212,7 @@ ev->click.button = MOUSE_BUTTON_NONE; ev->click.x = event->button.x; ev->click.y = event->button.y; + ev->click.clicks = event->button.clicks; for (size_t i = 0; buttons[i].value != MOUSE_BUTTON_NONE; ++i) { if (buttons[i].key == event->button.button) {
--- a/libmlk-core/core/event.h Tue Dec 15 22:07:18 2020 +0100 +++ b/libmlk-core/core/event.h Sun Dec 20 10:55:53 2020 +0100 @@ -50,6 +50,7 @@ enum mouse_button button; int x; int y; + unsigned int clicks; }; union event {
--- a/libmlk-core/core/save.c Tue Dec 15 22:07:18 2020 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,256 +0,0 @@ -/* - * save.c -- save functions - * - * Copyright (c) 2020 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 <compat.h> - -#include <assert.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -#include <sqlite3.h> - -#include <assets/sql/init.h> -#include <assets/sql/property-get.h> -#include <assets/sql/property-remove.h> -#include <assets/sql/property-set.h> - -#include "core_p.h" -#include "error.h" -#include "save.h" -#include "sys.h" -#include "util.h" - -#define SQL_BEGIN "BEGIN EXCLUSIVE TRANSACTION" -#define SQL_COMMIT "COMMIT" -#define SQL_ROLLBACK "ROLLBACK" - -static bool -exec(struct save *db, const char *sql) -{ - if (sqlite3_exec(db->handle, sql, NULL, NULL, NULL) != SQLITE_OK) - return errorf("%s", sqlite3_errmsg(db->handle)); - - return true; -} - -static const char * -path(unsigned int idx) -{ - return util_pathf("%s%u.db", sys_dir(SYS_DIR_SAVE), idx); -} - -static bool -execu(struct save *db, const unsigned char *sql) -{ - return exec(db, (const char *)sql); -} - -static bool -verify(struct save *db) -{ - struct { - time_t *date; - struct save_property prop; - } table[] = { - { .date = &db->created, { .key = "molko.create-date" } }, - { .date = &db->updated, { .key = "molko.update-date" } }, - }; - - /* Ensure create and update dates are present. */ - for (size_t i = 0; i < UTIL_SIZE(table); ++i) { - if (!save_get_property(db, &table[i].prop)) { - sqlite3_close(db->handle); - return errorf(_("database not initialized correctly")); - } - - *table[i].date = strtoull(table[i].prop.value, NULL, 10); - } - - return true; -} - -bool -save_open(struct save *db, unsigned int idx, enum save_mode mode) -{ - assert(db); - - return save_open_path(db, path(idx), mode); -} - -bool -save_open_path(struct save *db, const char *path, enum save_mode mode) -{ - assert(db); - assert(path); - - int flags = 0; - - switch (mode) { - case SAVE_MODE_WRITE: - flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE; - break; - default: - flags = SQLITE_OPEN_READONLY; - break; - } - - if (sqlite3_open_v2(path, (sqlite3**)&db->handle, flags, NULL) != SQLITE_OK) - goto sqlite3_err; - - if (mode == SAVE_MODE_WRITE && !execu(db, sql_init)) - goto sqlite3_err; - - return verify(db); - -sqlite3_err: - errorf("%s", sqlite3_errmsg(db->handle)); - sqlite3_close(db->handle); - - memset(db, 0, sizeof (*db)); - - return false; -} - -bool -save_ok(const struct save *db) -{ - assert(db); - - return db && db->handle; -} - -bool -save_set_property(struct save *db, const struct save_property *prop) -{ - assert(db); - assert(prop); - - sqlite3_stmt *stmt = NULL; - - if (!exec(db, SQL_BEGIN)) - return false; - if (sqlite3_prepare(db->handle, (const char *)sql_property_set, -1, &stmt, NULL) != SQLITE_OK) - goto sqlite3_err; - if (sqlite3_bind_text(stmt, 1, prop->key, -1, NULL) != SQLITE_OK || - sqlite3_bind_text(stmt, 2, prop->value, -1, NULL) != SQLITE_OK) - goto sqlite3_err; - if (sqlite3_step(stmt) != SQLITE_DONE) - goto sqlite3_err; - - sqlite3_finalize(stmt); - - return exec(db, SQL_COMMIT); - -sqlite3_err: - errorf("%s", sqlite3_errmsg(db->handle)); - - if (stmt) - sqlite3_finalize(stmt); - - exec(db, SQL_ROLLBACK); - - return false; -} - -bool -save_get_property(struct save *db, struct save_property *prop) -{ - assert(db); - assert(prop); - - sqlite3_stmt *stmt = NULL; - bool ret = true; - - if (sqlite3_prepare(db->handle, (const char *)sql_property_get, - sizeof (sql_property_get), &stmt, NULL) != SQLITE_OK) - goto sqlite3_err; - if (sqlite3_bind_text(stmt, 1, prop->key, -1, NULL) != SQLITE_OK) - goto sqlite3_err; - - switch (sqlite3_step(stmt)) { - case SQLITE_DONE: - /* Not found. */ - ret = errorf(_("property '%s' was not found"), prop->key); - break; - case SQLITE_ROW: - /* Found. */ - strlcpy(prop->value, (const char *)sqlite3_column_text(stmt, 0), - sizeof (prop->value)); - break; - default: - /* Error. */ - goto sqlite3_err; - } - - sqlite3_finalize(stmt); - - return ret; - -sqlite3_err: - errorf("%s", sqlite3_errmsg(db->handle)); - - if (stmt) - sqlite3_finalize(stmt); - - return false; -} - -bool -save_remove_property(struct save *db, const struct save_property *prop) -{ - assert(db); - assert(prop); - - sqlite3_stmt *stmt = NULL; - - if (!exec(db, SQL_BEGIN)) - return false; - if (sqlite3_prepare(db->handle, (const char *)sql_property_remove, - sizeof (sql_property_remove), &stmt, NULL) != SQLITE_OK) - goto sqlite3_err; - if (sqlite3_bind_text(stmt, 1, prop->key, -1, NULL) != SQLITE_OK) - goto sqlite3_err; - if (sqlite3_step(stmt) != SQLITE_DONE) - goto sqlite3_err; - - sqlite3_finalize(stmt); - - return exec(db, SQL_COMMIT); - -sqlite3_err: - errorf("%s", sqlite3_errmsg(db->handle)); - - if (stmt) - sqlite3_finalize(stmt); - - exec(db, SQL_ROLLBACK); - - return false; -} - -void -save_finish(struct save *db) -{ - assert(db); - - if (db->handle) - sqlite3_close(db->handle); - - memset(db, 0, sizeof (*db)); -}
--- a/libmlk-core/core/save.h Tue Dec 15 22:07:18 2020 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,65 +0,0 @@ -/* - * save.h -- save functions - * - * Copyright (c) 2020 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_CORE_SAVE_H -#define MOLKO_CORE_SAVE_H - -#include <stdbool.h> -#include <time.h> - -#define SAVE_PROPERTY_KEY_MAX (64) -#define SAVE_PROPERTY_VALUE_MAX (1024) - -struct save { - time_t created; - time_t updated; - void *handle; -}; - -enum save_mode { - SAVE_MODE_READ, - SAVE_MODE_WRITE -}; - -struct save_property { - char key[SAVE_PROPERTY_KEY_MAX + 1]; - char value[SAVE_PROPERTY_VALUE_MAX + 1]; -}; - -bool -save_open(struct save *db, unsigned int idx, enum save_mode mode); - -bool -save_open_path(struct save *db, const char *path, enum save_mode mode); - -bool -save_ok(const struct save *db); - -bool -save_set_property(struct save *db, const struct save_property *prop); - -bool -save_get_property(struct save *db, struct save_property *prop); - -bool -save_remove_property(struct save *db, const struct save_property *prop); - -void -save_finish(struct save *db); - -#endif /* !MOLKO_CORE_SAVE_H */
--- a/libmlk-core/nls/fr.po Tue Dec 15 22:07:18 2020 +0100 +++ b/libmlk-core/nls/fr.po Sun Dec 20 10:55:53 2020 +0100 @@ -8,7 +8,7 @@ msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-11-28 22:34+0100\n" +"POT-Creation-Date: 2020-12-20 10:24+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Language-Team: LANGUAGE <LL@li.org>\n" @@ -27,11 +27,9 @@ msgid "abort: panic handler returned\n" msgstr "fatal: la fonction de panique n'aurait pas du continuer\n" -#: /Users/markand/Dev/molko/libmlk-core/core/save.c:85 -msgid "database not initialized correctly" -msgstr "database non initialisée" +#~ msgid "database not initialized correctly" +#~ msgstr "database non initialisée" -#: /Users/markand/Dev/molko/libmlk-core/core/save.c:175 #, c-format -msgid "property '%s' was not found" -msgstr "propriété '%s' non trouvée" +#~ msgid "property '%s' was not found" +#~ msgstr "propriété '%s' non trouvée"
--- a/libmlk-core/nls/libmlk-core.pot Tue Dec 15 22:07:18 2020 +0100 +++ b/libmlk-core/nls/libmlk-core.pot Sun Dec 20 10:55:53 2020 +0100 @@ -8,7 +8,7 @@ msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-11-30 09:42+0100\n" +"POT-Creation-Date: 2020-12-20 10:24+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Language-Team: LANGUAGE <LL@li.org>\n" @@ -26,12 +26,3 @@ #, c-format msgid "abort: panic handler returned\n" msgstr "" - -#: /Users/markand/Dev/molko/libmlk-core/core/save.c:85 -msgid "database not initialized correctly" -msgstr "" - -#: /Users/markand/Dev/molko/libmlk-core/core/save.c:175 -#, c-format -msgid "property '%s' was not found" -msgstr ""
--- a/libmlk-rpg/CMakeLists.txt Tue Dec 15 22:07:18 2020 +0100 +++ b/libmlk-rpg/CMakeLists.txt Sun Dec 20 10:55:53 2020 +0100 @@ -19,55 +19,69 @@ project(libmlk-rpg) set( + ASSETS + ${libmlk-rpg_SOURCE_DIR}/assets/sql/character-save.sql + ${libmlk-rpg_SOURCE_DIR}/assets/sql/init.sql + ${libmlk-rpg_SOURCE_DIR}/assets/sql/property-get.sql + ${libmlk-rpg_SOURCE_DIR}/assets/sql/property-remove.sql + ${libmlk-rpg_SOURCE_DIR}/assets/sql/property-set.sql + ${libmlk-rpg_SOURCE_DIR}/assets/sql/character-load.sql +) + +set( SOURCES - ${libmlk-rpg_SOURCE_DIR}/rpg/battle.c - ${libmlk-rpg_SOURCE_DIR}/rpg/battle.h ${libmlk-rpg_SOURCE_DIR}/rpg/battle-bar.c ${libmlk-rpg_SOURCE_DIR}/rpg/battle-bar.h - ${libmlk-rpg_SOURCE_DIR}/rpg/battle-entity.c - ${libmlk-rpg_SOURCE_DIR}/rpg/battle-entity.h - ${libmlk-rpg_SOURCE_DIR}/rpg/battle-entity-state.c - ${libmlk-rpg_SOURCE_DIR}/rpg/battle-entity-state.h ${libmlk-rpg_SOURCE_DIR}/rpg/battle-entity-state-attacking.c ${libmlk-rpg_SOURCE_DIR}/rpg/battle-entity-state-blinking.c ${libmlk-rpg_SOURCE_DIR}/rpg/battle-entity-state-moving.c ${libmlk-rpg_SOURCE_DIR}/rpg/battle-entity-state-normal.c + ${libmlk-rpg_SOURCE_DIR}/rpg/battle-entity-state.c + ${libmlk-rpg_SOURCE_DIR}/rpg/battle-entity-state.h + ${libmlk-rpg_SOURCE_DIR}/rpg/battle-entity.c + ${libmlk-rpg_SOURCE_DIR}/rpg/battle-entity.h ${libmlk-rpg_SOURCE_DIR}/rpg/battle-indicator.c ${libmlk-rpg_SOURCE_DIR}/rpg/battle-indicator.h - ${libmlk-rpg_SOURCE_DIR}/rpg/battle-state.c - ${libmlk-rpg_SOURCE_DIR}/rpg/battle-state.h ${libmlk-rpg_SOURCE_DIR}/rpg/battle-state-ai.c ${libmlk-rpg_SOURCE_DIR}/rpg/battle-state-attacking.c ${libmlk-rpg_SOURCE_DIR}/rpg/battle-state-check.c ${libmlk-rpg_SOURCE_DIR}/rpg/battle-state-closing.c + ${libmlk-rpg_SOURCE_DIR}/rpg/battle-state-lost.c ${libmlk-rpg_SOURCE_DIR}/rpg/battle-state-menu.c - ${libmlk-rpg_SOURCE_DIR}/rpg/battle-state-lost.c ${libmlk-rpg_SOURCE_DIR}/rpg/battle-state-opening.c ${libmlk-rpg_SOURCE_DIR}/rpg/battle-state-selection.c ${libmlk-rpg_SOURCE_DIR}/rpg/battle-state-sub.c ${libmlk-rpg_SOURCE_DIR}/rpg/battle-state-victory.c + ${libmlk-rpg_SOURCE_DIR}/rpg/battle-state.c + ${libmlk-rpg_SOURCE_DIR}/rpg/battle-state.h + ${libmlk-rpg_SOURCE_DIR}/rpg/battle.c + ${libmlk-rpg_SOURCE_DIR}/rpg/battle.h ${libmlk-rpg_SOURCE_DIR}/rpg/character.c ${libmlk-rpg_SOURCE_DIR}/rpg/character.h ${libmlk-rpg_SOURCE_DIR}/rpg/equipment.c ${libmlk-rpg_SOURCE_DIR}/rpg/equipment.h ${libmlk-rpg_SOURCE_DIR}/rpg/item.c ${libmlk-rpg_SOURCE_DIR}/rpg/item.h + ${libmlk-rpg_SOURCE_DIR}/rpg/map-file.c + ${libmlk-rpg_SOURCE_DIR}/rpg/map-file.h ${libmlk-rpg_SOURCE_DIR}/rpg/map.c ${libmlk-rpg_SOURCE_DIR}/rpg/map.h - ${libmlk-rpg_SOURCE_DIR}/rpg/map-file.c - ${libmlk-rpg_SOURCE_DIR}/rpg/map-file.h ${libmlk-rpg_SOURCE_DIR}/rpg/message.c ${libmlk-rpg_SOURCE_DIR}/rpg/message.h ${libmlk-rpg_SOURCE_DIR}/rpg/rpg.c ${libmlk-rpg_SOURCE_DIR}/rpg/rpg.h ${libmlk-rpg_SOURCE_DIR}/rpg/rpg_p.h + ${libmlk-rpg_SOURCE_DIR}/rpg/save.c + ${libmlk-rpg_SOURCE_DIR}/rpg/save.h ${libmlk-rpg_SOURCE_DIR}/rpg/selection.h ${libmlk-rpg_SOURCE_DIR}/rpg/spell.c ${libmlk-rpg_SOURCE_DIR}/rpg/spell.h + ${libmlk-rpg_SOURCE_DIR}/rpg/team.c + ${libmlk-rpg_SOURCE_DIR}/rpg/team.h + ${libmlk-rpg_SOURCE_DIR}/rpg/tileset-file.c + ${libmlk-rpg_SOURCE_DIR}/rpg/tileset-file.h ${libmlk-rpg_SOURCE_DIR}/rpg/tileset.c ${libmlk-rpg_SOURCE_DIR}/rpg/tileset.h - ${libmlk-rpg_SOURCE_DIR}/rpg/tileset-file.c - ${libmlk-rpg_SOURCE_DIR}/rpg/tileset-file.h ${libmlk-rpg_SOURCE_DIR}/rpg/walksprite.c ${libmlk-rpg_SOURCE_DIR}/rpg/walksprite.h ) @@ -79,8 +93,9 @@ molko_define_library( TARGET libmlk-rpg + ASSETS ${ASSETS} TRANSLATIONS fr - SOURCES ${SOURCES} ${PO} + SOURCES ${SOURCES} ${PO} ${ASSETS} LIBRARIES PUBLIC libmlk-core @@ -91,6 +106,7 @@ INCLUDES PUBLIC $<BUILD_INTERFACE:${libmlk-rpg_SOURCE_DIR}> + $<BUILD_INTERFACE:${libmlk-rpg_BINARY_DIR}> ) source_group(TREE ${libmlk-rpg_SOURCE_DIR} FILES ${SOURCES})
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libmlk-rpg/assets/sql/character-load.sql Sun Dec 20 10:55:53 2020 +0100 @@ -0,0 +1,30 @@ +-- +-- character-load.sql -- load a character data +-- +-- Copyright (c) 2020 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 hp + , mp + , level + , team_order + , bonus_hp + , bonus_mp + , bonus_atk + , bonus_def + , bonus_agt + , bonus_luck + FROM character + WHERE name = ?
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libmlk-rpg/assets/sql/character-save.sql Sun Dec 20 10:55:53 2020 +0100 @@ -0,0 +1,44 @@ +-- +-- character-save.sql -- save or replace character +-- +-- Copyright (c) 2020 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 OR REPLACE INTO character( + name, + hp, + mp, + level, + team_order, + bonus_hp, + bonus_mp, + bonus_atk, + bonus_def, + bonus_agt, + bonus_luck +) +VALUES( + ?, + ?, + ?, + ?, + ?, + ?, + ?, + ?, + ?, + ?, + ? +)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libmlk-rpg/assets/sql/init.sql Sun Dec 20 10:55:53 2020 +0100 @@ -0,0 +1,44 @@ +-- +-- init.sql -- initialize database +-- +-- Copyright (c) 2020 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. +-- + +BEGIN EXCLUSIVE TRANSACTION; + +CREATE TABLE IF NOT EXISTS property( + id INTEGER PRIMARY KEY AUTOINCREMENT, + key TEXT NOT NULL UNIQUE, + value TEXT NOT NULL +); + +CREATE TABLE IF NOT EXISTS character( + name TEXT PRIMARY KEY, + hp INTEGER NOT NULL, + mp INTEGER NOT NULL, + level INTEGER NOT NULL, + team_order INTEGER DEFAULT -1, + bonus_hp INTEGER DEFAULT 0, + bonus_mp INTEGER DEFAULT 0, + bonus_atk INTEGER DEFAULT 0, + bonus_def INTEGER DEFAULT 0, + bonus_agt INTEGER DEFAULT 0, + bonus_luck INTEGER DEFAULT 0 +); + +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')); + +COMMIT;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libmlk-rpg/assets/sql/property-get.sql Sun Dec 20 10:55:53 2020 +0100 @@ -0,0 +1,21 @@ +-- +-- property-get.sql -- get a property +-- +-- Copyright (c) 2020 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 value + FROM property + WHERE key = ?
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libmlk-rpg/assets/sql/property-remove.sql Sun Dec 20 10:55:53 2020 +0100 @@ -0,0 +1,21 @@ +-- +-- property-remove.sql -- remove a property +-- +-- Copyright (c) 2020 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 property + WHERE key = ?
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libmlk-rpg/assets/sql/property-set.sql Sun Dec 20 10:55:53 2020 +0100 @@ -0,0 +1,26 @@ +-- +-- property-set.sql -- set a property +-- +-- Copyright (c) 2020 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 OR REPLACE INTO property( + key, + value +) +VALUES( + ?, + ? +)
--- a/libmlk-rpg/nls/fr.po Tue Dec 15 22:07:18 2020 +0100 +++ b/libmlk-rpg/nls/fr.po Sun Dec 20 10:55:53 2020 +0100 @@ -8,7 +8,7 @@ msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-11-30 09:48+0100\n" +"POT-Creation-Date: 2020-12-20 10:24+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Language-Team: LANGUAGE <LL@li.org>\n" @@ -33,33 +33,37 @@ msgid "Special" msgstr "" -#: /Users/markand/Dev/molko/libmlk-rpg/rpg/battle-state-victory.c:88 +#: /Users/markand/Dev/molko/libmlk-rpg/rpg/battle-state-victory.c:86 msgid "Victory!" msgstr "" -#: /Users/markand/Dev/molko/libmlk-rpg/rpg/battle-state-lost.c:87 +#: /Users/markand/Dev/molko/libmlk-rpg/rpg/battle-state-lost.c:85 msgid "You have been defeated..." msgstr "" -#: /Users/markand/Dev/molko/libmlk-rpg/rpg/tileset-file.c:241 +#: /Users/markand/Dev/molko/libmlk-rpg/rpg/tileset-file.c:240 msgid "could not parse image" msgstr "" -#: /Users/markand/Dev/molko/libmlk-rpg/rpg/map-file.c:130 +#: /Users/markand/Dev/molko/libmlk-rpg/rpg/map-file.c:148 msgid "could not parse tileset" msgstr "" -#: /Users/markand/Dev/molko/libmlk-rpg/rpg/map-file.c:92 -#, c-format -msgid "ignoring action %d,%d,%u,%u,%s" +#: /Users/markand/Dev/molko/libmlk-rpg/rpg/save.c:80 +msgid "database not initialized correctly" msgstr "" -#: /Users/markand/Dev/molko/libmlk-rpg/rpg/map-file.c:61 +#: /Users/markand/Dev/molko/libmlk-rpg/rpg/map-file.c:90 +#, c-format +msgid "ignoring action %d,%d,%u,%u,%d,%s" +msgstr "" + +#: /Users/markand/Dev/molko/libmlk-rpg/rpg/map-file.c:59 #, c-format msgid "invalid layer type: %s" msgstr "" -#: /Users/markand/Dev/molko/libmlk-rpg/rpg/map-file.c:175 +#: /Users/markand/Dev/molko/libmlk-rpg/rpg/map-file.c:193 msgid "invalid origin" msgstr "" @@ -81,54 +85,59 @@ msgid "message width too small: %u < %u" msgstr "" -#: /Users/markand/Dev/molko/libmlk-rpg/rpg/map-file.c:236 +#: /Users/markand/Dev/molko/libmlk-rpg/rpg/map-file.c:254 msgid "missing background layer" msgstr "" -#: /Users/markand/Dev/molko/libmlk-rpg/rpg/map-file.c:238 +#: /Users/markand/Dev/molko/libmlk-rpg/rpg/map-file.c:256 msgid "missing foreground layer" msgstr "" -#: /Users/markand/Dev/molko/libmlk-rpg/rpg/map-file.c:114 +#: /Users/markand/Dev/molko/libmlk-rpg/rpg/map-file.c:132 msgid "missing layer type definition" msgstr "" -#: /Users/markand/Dev/molko/libmlk-rpg/rpg/map-file.c:110 +#: /Users/markand/Dev/molko/libmlk-rpg/rpg/map-file.c:128 msgid "missing map dimensions before layer" msgstr "" -#: /Users/markand/Dev/molko/libmlk-rpg/rpg/tileset-file.c:239 +#: /Users/markand/Dev/molko/libmlk-rpg/rpg/tileset-file.c:238 msgid "missing tile dimensions before image" msgstr "" -#: /Users/markand/Dev/molko/libmlk-rpg/rpg/map-file.c:240 +#: /Users/markand/Dev/molko/libmlk-rpg/rpg/map-file.c:258 msgid "missing tileset" msgstr "" -#: /Users/markand/Dev/molko/libmlk-rpg/rpg/tileset-file.c:298 +#: /Users/markand/Dev/molko/libmlk-rpg/rpg/tileset-file.c:297 msgid "missing tileset image" msgstr "" -#: /Users/markand/Dev/molko/libmlk-rpg/rpg/map-file.c:229 +#: /Users/markand/Dev/molko/libmlk-rpg/rpg/map-file.c:247 msgid "missing title" msgstr "" -#: /Users/markand/Dev/molko/libmlk-rpg/rpg/map-file.c:157 +#: /Users/markand/Dev/molko/libmlk-rpg/rpg/map-file.c:175 msgid "null map columns" msgstr "" -#: /Users/markand/Dev/molko/libmlk-rpg/rpg/map-file.c:166 +#: /Users/markand/Dev/molko/libmlk-rpg/rpg/map-file.c:184 msgid "null map rows" msgstr "" -#: /Users/markand/Dev/molko/libmlk-rpg/rpg/map-file.c:146 +#: /Users/markand/Dev/molko/libmlk-rpg/rpg/map-file.c:164 msgid "null map title" msgstr "" -#: /Users/markand/Dev/molko/libmlk-rpg/rpg/tileset-file.c:142 +#: /Users/markand/Dev/molko/libmlk-rpg/rpg/save.c:265 +#, c-format +msgid "property '%s' was not found" +msgstr "" + +#: /Users/markand/Dev/molko/libmlk-rpg/rpg/tileset-file.c:141 msgid "tileheight is null" msgstr "" -#: /Users/markand/Dev/molko/libmlk-rpg/rpg/tileset-file.c:133 +#: /Users/markand/Dev/molko/libmlk-rpg/rpg/tileset-file.c:132 msgid "tilewidth is null" msgstr ""
--- a/libmlk-rpg/nls/libmlk-rpg.pot Tue Dec 15 22:07:18 2020 +0100 +++ b/libmlk-rpg/nls/libmlk-rpg.pot Sun Dec 20 10:55:53 2020 +0100 @@ -8,7 +8,7 @@ msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-11-30 09:48+0100\n" +"POT-Creation-Date: 2020-12-20 10:24+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Language-Team: LANGUAGE <LL@li.org>\n" @@ -33,33 +33,37 @@ msgid "Special" msgstr "" -#: /Users/markand/Dev/molko/libmlk-rpg/rpg/battle-state-victory.c:88 +#: /Users/markand/Dev/molko/libmlk-rpg/rpg/battle-state-victory.c:86 msgid "Victory!" msgstr "" -#: /Users/markand/Dev/molko/libmlk-rpg/rpg/battle-state-lost.c:87 +#: /Users/markand/Dev/molko/libmlk-rpg/rpg/battle-state-lost.c:85 msgid "You have been defeated..." msgstr "" -#: /Users/markand/Dev/molko/libmlk-rpg/rpg/tileset-file.c:241 +#: /Users/markand/Dev/molko/libmlk-rpg/rpg/tileset-file.c:240 msgid "could not parse image" msgstr "" -#: /Users/markand/Dev/molko/libmlk-rpg/rpg/map-file.c:130 +#: /Users/markand/Dev/molko/libmlk-rpg/rpg/map-file.c:148 msgid "could not parse tileset" msgstr "" -#: /Users/markand/Dev/molko/libmlk-rpg/rpg/map-file.c:92 -#, c-format -msgid "ignoring action %d,%d,%u,%u,%s" +#: /Users/markand/Dev/molko/libmlk-rpg/rpg/save.c:80 +msgid "database not initialized correctly" msgstr "" -#: /Users/markand/Dev/molko/libmlk-rpg/rpg/map-file.c:61 +#: /Users/markand/Dev/molko/libmlk-rpg/rpg/map-file.c:90 +#, c-format +msgid "ignoring action %d,%d,%u,%u,%d,%s" +msgstr "" + +#: /Users/markand/Dev/molko/libmlk-rpg/rpg/map-file.c:59 #, c-format msgid "invalid layer type: %s" msgstr "" -#: /Users/markand/Dev/molko/libmlk-rpg/rpg/map-file.c:175 +#: /Users/markand/Dev/molko/libmlk-rpg/rpg/map-file.c:193 msgid "invalid origin" msgstr "" @@ -81,54 +85,59 @@ msgid "message width too small: %u < %u" msgstr "" -#: /Users/markand/Dev/molko/libmlk-rpg/rpg/map-file.c:236 +#: /Users/markand/Dev/molko/libmlk-rpg/rpg/map-file.c:254 msgid "missing background layer" msgstr "" -#: /Users/markand/Dev/molko/libmlk-rpg/rpg/map-file.c:238 +#: /Users/markand/Dev/molko/libmlk-rpg/rpg/map-file.c:256 msgid "missing foreground layer" msgstr "" -#: /Users/markand/Dev/molko/libmlk-rpg/rpg/map-file.c:114 +#: /Users/markand/Dev/molko/libmlk-rpg/rpg/map-file.c:132 msgid "missing layer type definition" msgstr "" -#: /Users/markand/Dev/molko/libmlk-rpg/rpg/map-file.c:110 +#: /Users/markand/Dev/molko/libmlk-rpg/rpg/map-file.c:128 msgid "missing map dimensions before layer" msgstr "" -#: /Users/markand/Dev/molko/libmlk-rpg/rpg/tileset-file.c:239 +#: /Users/markand/Dev/molko/libmlk-rpg/rpg/tileset-file.c:238 msgid "missing tile dimensions before image" msgstr "" -#: /Users/markand/Dev/molko/libmlk-rpg/rpg/map-file.c:240 +#: /Users/markand/Dev/molko/libmlk-rpg/rpg/map-file.c:258 msgid "missing tileset" msgstr "" -#: /Users/markand/Dev/molko/libmlk-rpg/rpg/tileset-file.c:298 +#: /Users/markand/Dev/molko/libmlk-rpg/rpg/tileset-file.c:297 msgid "missing tileset image" msgstr "" -#: /Users/markand/Dev/molko/libmlk-rpg/rpg/map-file.c:229 +#: /Users/markand/Dev/molko/libmlk-rpg/rpg/map-file.c:247 msgid "missing title" msgstr "" -#: /Users/markand/Dev/molko/libmlk-rpg/rpg/map-file.c:157 +#: /Users/markand/Dev/molko/libmlk-rpg/rpg/map-file.c:175 msgid "null map columns" msgstr "" -#: /Users/markand/Dev/molko/libmlk-rpg/rpg/map-file.c:166 +#: /Users/markand/Dev/molko/libmlk-rpg/rpg/map-file.c:184 msgid "null map rows" msgstr "" -#: /Users/markand/Dev/molko/libmlk-rpg/rpg/map-file.c:146 +#: /Users/markand/Dev/molko/libmlk-rpg/rpg/map-file.c:164 msgid "null map title" msgstr "" -#: /Users/markand/Dev/molko/libmlk-rpg/rpg/tileset-file.c:142 +#: /Users/markand/Dev/molko/libmlk-rpg/rpg/save.c:265 +#, c-format +msgid "property '%s' was not found" +msgstr "" + +#: /Users/markand/Dev/molko/libmlk-rpg/rpg/tileset-file.c:141 msgid "tileheight is null" msgstr "" -#: /Users/markand/Dev/molko/libmlk-rpg/rpg/tileset-file.c:133 +#: /Users/markand/Dev/molko/libmlk-rpg/rpg/tileset-file.c:132 msgid "tilewidth is null" msgstr ""
--- a/libmlk-rpg/rpg/character.c Tue Dec 15 22:07:18 2020 +0100 +++ b/libmlk-rpg/rpg/character.c Sun Dec 20 10:55:53 2020 +0100 @@ -20,11 +20,15 @@ #include <core/sprite.h> +#include <assets/sql/character-save.h> +#include <assets/sql/character-load.h> + #include "character.h" #include "equipment.h" +#include "save.h" bool -character_ok(struct character *ch) +character_ok(const struct character *ch) { return ch && ch->name && ch->type && ch->reset && sprite_ok(ch->sprites[CHARACTER_SPRITE_NORMAL]); } @@ -65,3 +69,54 @@ if (ch->exec) ch->exec(ch, bt); } + +bool +character_save(const struct character *ch, struct save *s) +{ + assert(ch); + assert(save_ok(s)); + + return save_exec(s, (const char *)sql_character_save, "s iii i iiiiii", + ch->name, + ch->hp, + ch->mp, + ch->level, + ch->team_order, + ch->hpbonus, + ch->mpbonus, + ch->atkbonus, + ch->defbonus, + ch->agtbonus, + ch->luckbonus + ); +} + +bool +character_load(struct character *ch, struct save *s) +{ + assert(ch); + assert(save_ok(s)); + + struct save_stmt stmt; + bool ret; + + if (!save_stmt_init(s, &stmt, (const char *)sql_character_load, "s", ch->name)) + return false; + + ret = save_stmt_next(&stmt, "iii i iiiiii", + &ch->hp, + &ch->mp, + &ch->level, + &ch->team_order, + &ch->hpbonus, + &ch->mpbonus, + &ch->atkbonus, + &ch->defbonus, + &ch->agtbonus, + &ch->luckbonus + ); + + save_stmt_finish(&stmt); + + return ret; +}
--- a/libmlk-rpg/rpg/character.h Tue Dec 15 22:07:18 2020 +0100 +++ b/libmlk-rpg/rpg/character.h Sun Dec 20 10:55:53 2020 +0100 @@ -19,175 +19,88 @@ #ifndef MOLKO_RPG_CHARACTER_H #define MOLKO_RPG_CHARACTER_H -/** - * \file character.h - * \brief Character definition. - */ - #include <stdbool.h> +#define CHARACTER_SPELL_MAX (64) + struct battle; +struct save; struct sprite; struct spell; -/** - * \brief Maximum number of spells in a character. - */ -#define CHARACTER_SPELL_MAX (64) - -/** - * \brief Character status - */ enum character_status { - CHARACTER_STATUS_NORMAL, /*!< No status. */ - CHARACTER_STATUS_POISON = (1 << 0) /*!< Character is poisoned. */ + CHARACTER_STATUS_NORMAL, + CHARACTER_STATUS_POISON = (1 << 0) }; -/** - * \brief Sprites per action. - * - * This enumeration should be synced with \ref equipment_type. - */ enum character_sprite { - CHARACTER_SPRITE_AXE, /*!< Attacking with axe. */ - CHARACTER_SPRITE_BOW, /*!< Attacking with bow. */ - CHARACTER_SPRITE_CROSSBOW, /*!< Attacking with crossbow. */ - CHARACTER_SPRITE_DAGGER, /*!< Attacking with dagger. */ - CHARACTER_SPRITE_HAMMER, /*!< Attacking with hammer. */ - CHARACTER_SPRITE_NORMAL, /*!< Sprite for walking. */ - CHARACTER_SPRITE_SPIKE, /*!< Attacking with spike. */ - CHARACTER_SPRITE_SWORD, /*!< Attacking with sword. */ - CHARACTER_SPRITE_WAND, /*!< Attacking with wand. */ - CHARACTER_SPRITE_NUM /*!< Total number of sprites. */ -}; - -/** - * \brief Equipment per character. - * - * This enumeration should be synced with \ref equipment_type. - */ -enum character_equipment { - CHARACTER_EQUIPMENT_GLOVES, /*!< Gloves equiped. */ - CHARACTER_EQUIPMENT_HELMET, /*!< Helmet equiped. */ - CHARACTER_EQUIPMENT_SHIELD, /*!< Shield equiped. */ - CHARACTER_EQUIPMENT_TOP, /*!< Top equiped. */ - CHARACTER_EQUIPMENT_TROUSERS, /*!< Trousers equiped. */ - CHARACTER_EQUIPMENT_WEAPON, /*!< Weapon equiped. */ - CHARACTER_EQUIPMENT_NUM /*!< Total number of equipments equiped. */ + CHARACTER_SPRITE_AXE, + CHARACTER_SPRITE_BOW, + CHARACTER_SPRITE_CROSSBOW, + CHARACTER_SPRITE_DAGGER, + CHARACTER_SPRITE_HAMMER, + CHARACTER_SPRITE_NORMAL, + CHARACTER_SPRITE_SPIKE, + CHARACTER_SPRITE_SWORD, + CHARACTER_SPRITE_WAND, + CHARACTER_SPRITE_NUM }; -/** - * \brief Character object - * - * This structure owns the current character statistics used in battle. - */ +enum character_equipment { + CHARACTER_EQUIPMENT_GLOVES, + CHARACTER_EQUIPMENT_HELMET, + CHARACTER_EQUIPMENT_SHIELD, + CHARACTER_EQUIPMENT_TOP, + CHARACTER_EQUIPMENT_TROUSERS, + CHARACTER_EQUIPMENT_WEAPON, + CHARACTER_EQUIPMENT_NUM +}; + struct character { - const char *name; /*!< (+) Character name. */ - const char *type; /*!< (+) Type or class name. */ - unsigned int level; /*!< (+) Character level. */ - enum character_status status; /*!< (+) Character status. */ - int hp; /*!< (+) Heal points. */ - unsigned int hpmax; /*!< (+) Maximum heal points. */ - unsigned int hpbonus; /*!< (+) User heal points bonus. */ - int mp; /*!< (+) Magic points. */ - unsigned int mpmax; /*!< (+) Maximum magic points. */ - unsigned int mpbonus; /*!< (+) User magic points bonus. */ - int atk; /*!< (+) Current attack points (increase fire based spells too). */ - unsigned int atkbonus; /*!< (+) User attack bonus. */ - int def; /*!< (+) Current defense points (increase earth based spells too). */ - unsigned int defbonus; /*!< (+) User defense bonus. */ - int agt; /*!< (+) Current agility (increase wind based spells too). */ - unsigned int agtbonus; /*!< (+) User agility bonus. */ - int luck; /*!< (+) Current luck points (increase */ - unsigned int luckbonus; /*!< (+) User luck bonus. */ + const char *name; + const char *type; + unsigned int level; + enum character_status status; + int hp; + unsigned int hpmax; + unsigned int hpbonus; + int mp; + unsigned int mpmax; + unsigned int mpbonus; + int atk; + unsigned int atkbonus; + int def; + unsigned int defbonus; + int agt; + unsigned int agtbonus; + int luck; + unsigned int luckbonus; + unsigned int team_order; - /** - * (+&) Sprites to use. - */ struct sprite *sprites[CHARACTER_SPRITE_NUM]; - - /** - * (+&) Equipments for this character. - */ const struct equipment *equipments[CHARACTER_EQUIPMENT_NUM]; - - /** - * (+&?) List of spells for this character. - */ const struct spell *spells[CHARACTER_SPELL_MAX]; - /** - * (+) Reset statistics from this character class. - * - * This function must reset the following member variables according - * to the class characteristics: - * - * - hpmax - * - mpmax - * - atk - * - def - * - agt - * - luck - * - * \param owner this owner - */ void (*reset)(struct character *owner); - - /** - * (+?) Execute an action. - * - * This function should be present for AI enemies in a battle, it should - * be kept NULL for team players unless they have automatic actions - * which in that case would skip user input. - * - * \param owner this owner - * \param bt the battle object - */ void (*exec)(struct character *owner, struct battle *bt); }; -/** - * Check if this is a valid character object. - * - * \pre ch != NULL - * \param ch the character object - */ bool -character_ok(struct character *ch); +character_ok(const struct character *ch); -/** - * Get a string name for the given status. - * - * Since status is a bitmask you have to select only one status. - * - * \pre status must be valid - * \param status the status - * \return A const string. - */ const char * character_status_string(enum character_status status); -/** - * Shortcut for ch->reset. - * - * This function is usually called after an equipment change, a level change - * or and of a battle. - * - * \pre ch != NULL - * \param ch the character object - */ void character_reset(struct character *ch); -/** - * Shortcut for ch->exec (if not NULL) - * - * \pre character_ok(ch) - * \pre bt != NULL - * \param ch the character - * \param bt the battle object - */ void character_exec(struct character *ch, struct battle *bt); +bool +character_save(const struct character *ch, struct save *s); + +bool +character_load(struct character *, struct save *); + #endif /* !MOLKO_RPG_CHARACTER_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libmlk-rpg/rpg/save.c Sun Dec 20 10:55:53 2020 +0100 @@ -0,0 +1,408 @@ +/* + * save.c -- save functions + * + * Copyright (c) 2020 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 <compat.h> + +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <sqlite3.h> + +#include <core/error.h> +#include <core/sys.h> +#include <core/util.h> + +#include <assets/sql/init.h> +#include <assets/sql/property-get.h> +#include <assets/sql/property-remove.h> +#include <assets/sql/property-set.h> + +#include "rpg_p.h" +#include "save.h" + +#define SQL_BEGIN "BEGIN EXCLUSIVE TRANSACTION" +#define SQL_COMMIT "COMMIT" +#define SQL_ROLLBACK "ROLLBACK" + +static bool +exec(struct save *db, const char *sql) +{ + if (sqlite3_exec(db->handle, sql, NULL, NULL, NULL) != SQLITE_OK) + return errorf("%s", sqlite3_errmsg(db->handle)); + + return true; +} + +static const char * +path(unsigned int idx) +{ + return util_pathf("%s%u.db", sys_dir(SYS_DIR_SAVE), idx); +} + +static bool +execu(struct save *db, const unsigned char *sql) +{ + return exec(db, (const char *)sql); +} + +static bool +verify(struct save *db) +{ + struct { + time_t *date; + struct save_property prop; + } table[] = { + { .date = &db->created, { .key = "molko.create-date" } }, + { .date = &db->updated, { .key = "molko.update-date" } }, + }; + + /* Ensure create and update dates are present. */ + for (size_t i = 0; i < UTIL_SIZE(table); ++i) { + if (!save_get_property(db, &table[i].prop)) { + sqlite3_close(db->handle); + return errorf(_("database not initialized correctly")); + } + + *table[i].date = strtoull(table[i].prop.value, NULL, 10); + } + + return true; +} + +static bool +prepare(struct save *s, struct save_stmt *stmt, const char *sql, const char *args, va_list ap) +{ + stmt->parent = s; + stmt->handle = NULL; + + if (sqlite3_prepare(s->handle, sql, -1, (sqlite3_stmt **)&stmt->handle, NULL) != SQLITE_OK) + goto sqlite3_err; + + for (int i = 1; args && *args; ++args) { + switch (*args) { + case 'i': + case 'u': + if (sqlite3_bind_int(stmt->handle, i++, va_arg(ap, int)) != SQLITE_OK) + return false; + break; + case 's': + if (sqlite3_bind_text(stmt->handle, i++, va_arg(ap, const char *), -1, NULL) != SQLITE_OK) + return false; + break; + case 't': + if (sqlite3_bind_int64(stmt->handle, i++, va_arg(ap, time_t)) != SQLITE_OK) + return false; + break; + case ' ': + break; + default: + return errorf("invalid format: %c", *args); + } + } + + return true; + +sqlite3_err: + return errorf("%s", sqlite3_errmsg(s->handle)); +} + +static bool +extract(struct save_stmt *stmt, const char *args, va_list ap) +{ + const int ncols = sqlite3_column_count(stmt->handle); + + for (int c = 0; args && *args; ++args) { + if (c >= ncols) + return errorf("too many arguments"); + + /* TODO: type check. */ + switch (*args) { + case 'i': + case 'u': + *va_arg(ap, int *) = sqlite3_column_int(stmt->handle, c++); + break; + case 's': { + char *str = va_arg(ap, char *); + size_t max = va_arg(ap, size_t); + + strlcpy(str, (const char *)sqlite3_column_text(stmt->handle, c++), max); + break; + } + case 't': + *va_arg(ap, time_t *) = sqlite3_column_int64(stmt->handle, c++); + break; + case ' ': + break; + default: + return errorf("invalid format: %c", *args); + } + } + + return true; + +sqlite3_err: + return errorf("%s", sqlite3_errmsg(stmt->parent->handle)); +} + +bool +save_open(struct save *db, unsigned int idx, enum save_mode mode) +{ + assert(db); + + return save_open_path(db, path(idx), mode); +} + +bool +save_open_path(struct save *db, const char *path, enum save_mode mode) +{ + assert(db); + assert(path); + + int flags = 0; + + switch (mode) { + case SAVE_MODE_WRITE: + flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE; + break; + default: + flags = SQLITE_OPEN_READONLY; + break; + } + + if (sqlite3_open_v2(path, (sqlite3**)&db->handle, flags, NULL) != SQLITE_OK) + goto sqlite3_err; + + if (mode == SAVE_MODE_WRITE && !execu(db, sql_init)) + goto sqlite3_err; + + return verify(db); + +sqlite3_err: + errorf("%s", sqlite3_errmsg(db->handle)); + sqlite3_close(db->handle); + + memset(db, 0, sizeof (*db)); + + return false; +} + +bool +save_ok(const struct save *db) +{ + assert(db); + + return db && db->handle; +} + +bool +save_set_property(struct save *db, const struct save_property *prop) +{ + assert(db); + assert(prop); + + sqlite3_stmt *stmt = NULL; + + if (!exec(db, SQL_BEGIN)) + return false; + if (sqlite3_prepare(db->handle, (const char *)sql_property_set, -1, &stmt, NULL) != SQLITE_OK) + goto sqlite3_err; + if (sqlite3_bind_text(stmt, 1, prop->key, -1, NULL) != SQLITE_OK || + sqlite3_bind_text(stmt, 2, prop->value, -1, NULL) != SQLITE_OK) + goto sqlite3_err; + if (sqlite3_step(stmt) != SQLITE_DONE) + goto sqlite3_err; + + sqlite3_finalize(stmt); + + return exec(db, SQL_COMMIT); + +sqlite3_err: + errorf("%s", sqlite3_errmsg(db->handle)); + + if (stmt) + sqlite3_finalize(stmt); + + exec(db, SQL_ROLLBACK); + + return false; +} + +bool +save_get_property(struct save *db, struct save_property *prop) +{ + assert(db); + assert(prop); + + sqlite3_stmt *stmt = NULL; + bool ret = true; + + if (sqlite3_prepare(db->handle, (const char *)sql_property_get, + sizeof (sql_property_get), &stmt, NULL) != SQLITE_OK) + goto sqlite3_err; + if (sqlite3_bind_text(stmt, 1, prop->key, -1, NULL) != SQLITE_OK) + goto sqlite3_err; + + switch (sqlite3_step(stmt)) { + case SQLITE_DONE: + /* Not found. */ + ret = errorf(_("property '%s' was not found"), prop->key); + break; + case SQLITE_ROW: + /* Found. */ + strlcpy(prop->value, (const char *)sqlite3_column_text(stmt, 0), + sizeof (prop->value)); + break; + default: + /* Error. */ + goto sqlite3_err; + } + + sqlite3_finalize(stmt); + + return ret; + +sqlite3_err: + errorf("%s", sqlite3_errmsg(db->handle)); + + if (stmt) + sqlite3_finalize(stmt); + + return false; +} + +bool +save_remove_property(struct save *db, const struct save_property *prop) +{ + assert(db); + assert(prop); + + sqlite3_stmt *stmt = NULL; + + if (!exec(db, SQL_BEGIN)) + return false; + if (sqlite3_prepare(db->handle, (const char *)sql_property_remove, + sizeof (sql_property_remove), &stmt, NULL) != SQLITE_OK) + goto sqlite3_err; + if (sqlite3_bind_text(stmt, 1, prop->key, -1, NULL) != SQLITE_OK) + goto sqlite3_err; + if (sqlite3_step(stmt) != SQLITE_DONE) + goto sqlite3_err; + + sqlite3_finalize(stmt); + + return exec(db, SQL_COMMIT); + +sqlite3_err: + errorf("%s", sqlite3_errmsg(db->handle)); + + if (stmt) + sqlite3_finalize(stmt); + + exec(db, SQL_ROLLBACK); + + return false; +} + +bool +save_exec(struct save *db, const char *sql, const char *args, ...) +{ + assert(save_ok(db)); + assert(sql && args); + + struct save_stmt stmt; + bool ret; + va_list ap; + + va_start(ap, args); + ret = prepare(db, &stmt, sql, args, ap); + va_end(ap); + + if (!ret) + return false; + + ret = save_stmt_next(&stmt, NULL) == 0; + save_stmt_finish(&stmt); + + return ret; +} + +void +save_finish(struct save *db) +{ + assert(db); + + if (db->handle) + sqlite3_close(db->handle); + + memset(db, 0, sizeof (*db)); +} + +bool +save_stmt_init(struct save *db, struct save_stmt *stmt, const char *sql, const char *args, ...) +{ + assert(save_ok(db)); + assert(stmt); + assert(args); + + va_list ap; + bool ret; + + va_start(ap, args); + ret = prepare(db, stmt, sql, args, ap); + va_end(ap); + + return ret; +} + +int +save_stmt_next(struct save_stmt *stmt, const char *args, ...) +{ + assert(stmt); + + va_list ap; + bool ret = -1; + + switch (sqlite3_step(stmt->handle)) { + case SQLITE_ROW: + va_start(ap, args); + + if (extract(stmt, args, ap)) + ret = 1; + + va_end(ap); + break; + case SQLITE_DONE: + ret = 0; + break; + default: + break; + } + + return ret; +} + +void +save_stmt_finish(struct save_stmt *stmt) +{ + assert(stmt); + + sqlite3_finalize(stmt->handle); + memset(stmt, 0, sizeof (*stmt)); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libmlk-rpg/rpg/save.h Sun Dec 20 10:55:53 2020 +0100 @@ -0,0 +1,83 @@ +/* + * save.h -- save functions + * + * Copyright (c) 2020 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_SAVE_H +#define MOLKO_RPG_SAVE_H + +#include <stdbool.h> +#include <time.h> + +#define SAVE_PROPERTY_KEY_MAX (64) +#define SAVE_PROPERTY_VALUE_MAX (1024) + +struct save { + time_t created; + time_t updated; + void *handle; +}; + +enum save_mode { + SAVE_MODE_READ, + SAVE_MODE_WRITE +}; + +struct save_property { + char key[SAVE_PROPERTY_KEY_MAX + 1]; + char value[SAVE_PROPERTY_VALUE_MAX + 1]; +}; + +struct save_stmt { + struct save *parent; + void *handle; +}; + +bool +save_open(struct save *db, unsigned int idx, enum save_mode mode); + +bool +save_open_path(struct save *db, const char *path, enum save_mode mode); + +bool +save_ok(const struct save *db); + +bool +save_set_property(struct save *db, const struct save_property *prop); + +bool +save_get_property(struct save *db, struct save_property *prop); + +bool +save_remove_property(struct save *db, const struct save_property *prop); + +bool +save_exec(struct save *db, const char *sql, const char *args, ...); + +void +save_finish(struct save *db); + +/* Prepared statements. */ +bool +save_stmt_init(struct save *db, struct save_stmt *stmt, const char *sql, const char *args, ...); + +int +save_stmt_next(struct save_stmt *stmt, const char *args, ...); + +void +save_stmt_finish(struct save_stmt *stmt); + +#endif /* !MOLKO_RPG_SAVE_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libmlk-rpg/rpg/team.c Sun Dec 20 10:55:53 2020 +0100 @@ -0,0 +1,19 @@ +/* + * team.c -- team storage + * + * Copyright (c) 2020 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. + */ + +/* Nothing yet. */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libmlk-rpg/rpg/team.h Sun Dec 20 10:55:53 2020 +0100 @@ -0,0 +1,30 @@ +/* + * team.h -- team storage + * + * Copyright (c) 2020 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_TEAM_H +#define MOLKO_RPG_TEAM_H + +#define TEAM_MEMBER_MAX (4) + +struct character; + +struct team { + struct character *members[TEAM_MEMBER_MAX]; +}; + +#endif /* MOLKO_RPG_TEAM_H */
--- a/tests/CMakeLists.txt Tue Dec 15 22:07:18 2020 +0100 +++ b/tests/CMakeLists.txt Sun Dec 20 10:55:53 2020 +0100 @@ -21,6 +21,7 @@ molko_define_test(TARGET action SOURCES test-action.c) molko_define_test(TARGET action-script SOURCES test-action-script.c) molko_define_test(TARGET alloc SOURCES test-alloc.c) +molko_define_test(TARGET character SOURCES test-character.c) molko_define_test(TARGET color SOURCES test-color.c) molko_define_test(TARGET error SOURCES test-error.c)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-character.c Sun Dec 20 10:55:53 2020 +0100 @@ -0,0 +1,90 @@ +/* + * test-character.c -- test character routines + * + * Copyright (c) 2020 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> +#include <string.h> + +#define GREATEST_USE_ABBREVS 0 +#include <greatest.h> + +#include <rpg/character.h> +#include <rpg/save.h> + +static void +clean(void *data) +{ + (void)data; + + remove("test.db"); +} + +GREATEST_TEST +test_save_simple(void) +{ + struct save db; + struct character ch = { + .name = "david", + .hp = 1989, + .mp = 1, + .level = 18, + .team_order = 1, + .hpbonus = 500, + .mpbonus = 50, + .atkbonus = 1001, + .defbonus = 1002, + .agtbonus = 1003, + .luckbonus = 1004 + }; + + GREATEST_ASSERT(save_open_path(&db, "test.db", SAVE_MODE_WRITE)); + GREATEST_ASSERT(character_save(&ch, &db)); + + /* Restore. */ + memset(&ch, 0, sizeof (ch)); + ch.name = "david"; + + GREATEST_ASSERT(character_load(&ch, &db)); + GREATEST_ASSERT_EQ(1989, ch.hp); + GREATEST_ASSERT_EQ(1, ch.mp); + GREATEST_ASSERT_EQ(18, ch.level); + GREATEST_ASSERT_EQ(1, ch.team_order); + GREATEST_ASSERT_EQ(500, ch.hpbonus); + GREATEST_ASSERT_EQ(50, ch.mpbonus); + GREATEST_ASSERT_EQ(1001, ch.atkbonus); + GREATEST_ASSERT_EQ(1002, ch.defbonus); + GREATEST_ASSERT_EQ(1003, ch.agtbonus); + GREATEST_ASSERT_EQ(1004, ch.luckbonus); + GREATEST_PASS(); +} + +GREATEST_SUITE(suite_save) +{ + GREATEST_SET_SETUP_CB(clean, NULL); + GREATEST_SET_TEARDOWN_CB(clean, NULL); + GREATEST_RUN_TEST(test_save_simple); +} + +GREATEST_MAIN_DEFS(); + +int +main(int argc, char **argv) +{ + GREATEST_MAIN_BEGIN(); + GREATEST_RUN_SUITE(suite_save); + GREATEST_MAIN_END(); +}