changeset 434:4e78f045e8c0

rpg: cleanup hierarchy
author David Demelier <markand@malikania.fr>
date Sat, 15 Oct 2022 21:24:17 +0200
parents 862b15c3a3ae
children 556c8e2ff995
files GNUmakefile examples/example-action/example-action.c examples/example-message/example-message.c examples/example/character-john.c examples/example/spell-fire.c libmlk-rpg/assets/sql/character-load.sql libmlk-rpg/assets/sql/character-save.sql libmlk-rpg/assets/sql/init.sql libmlk-rpg/assets/sql/property-load.sql libmlk-rpg/assets/sql/property-remove.sql libmlk-rpg/assets/sql/property-save.sql libmlk-rpg/assets/sql/quest-remove.sql libmlk-rpg/assets/sql/quest-save.sql libmlk-rpg/assets/sql/quest-step-load.sql libmlk-rpg/assets/sql/quest-step-save.sql libmlk-rpg/mlk/rpg/battle-bar-default.c libmlk-rpg/mlk/rpg/battle-bar-default.h libmlk-rpg/mlk/rpg/battle-bar.c libmlk-rpg/mlk/rpg/battle-bar.h libmlk-rpg/mlk/rpg/battle-entity-state-attacking.c libmlk-rpg/mlk/rpg/battle-entity-state-attacking.h libmlk-rpg/mlk/rpg/battle-entity-state-blinking.c libmlk-rpg/mlk/rpg/battle-entity-state-blinking.h libmlk-rpg/mlk/rpg/battle-entity-state-moving.c libmlk-rpg/mlk/rpg/battle-entity-state-moving.h libmlk-rpg/mlk/rpg/battle-entity-state-normal.c libmlk-rpg/mlk/rpg/battle-entity-state-normal.h libmlk-rpg/mlk/rpg/battle-entity-state.c libmlk-rpg/mlk/rpg/battle-entity-state.h libmlk-rpg/mlk/rpg/battle-entity.c libmlk-rpg/mlk/rpg/battle-entity.h libmlk-rpg/mlk/rpg/battle-indicator.c libmlk-rpg/mlk/rpg/battle-indicator.h libmlk-rpg/mlk/rpg/battle-message.c libmlk-rpg/mlk/rpg/battle-message.h libmlk-rpg/mlk/rpg/battle-state-ai.c libmlk-rpg/mlk/rpg/battle-state-ai.h libmlk-rpg/mlk/rpg/battle-state-attacking.c libmlk-rpg/mlk/rpg/battle-state-attacking.h libmlk-rpg/mlk/rpg/battle-state-check.c libmlk-rpg/mlk/rpg/battle-state-check.h libmlk-rpg/mlk/rpg/battle-state-closing.c libmlk-rpg/mlk/rpg/battle-state-closing.h libmlk-rpg/mlk/rpg/battle-state-item.c libmlk-rpg/mlk/rpg/battle-state-item.h libmlk-rpg/mlk/rpg/battle-state-lost.c libmlk-rpg/mlk/rpg/battle-state-lost.h libmlk-rpg/mlk/rpg/battle-state-menu.c libmlk-rpg/mlk/rpg/battle-state-menu.h libmlk-rpg/mlk/rpg/battle-state-opening.c libmlk-rpg/mlk/rpg/battle-state-opening.h libmlk-rpg/mlk/rpg/battle-state-rendering.c libmlk-rpg/mlk/rpg/battle-state-rendering.h libmlk-rpg/mlk/rpg/battle-state-selection.c libmlk-rpg/mlk/rpg/battle-state-selection.h libmlk-rpg/mlk/rpg/battle-state-victory.c libmlk-rpg/mlk/rpg/battle-state-victory.h libmlk-rpg/mlk/rpg/battle-state.c libmlk-rpg/mlk/rpg/battle-state.h libmlk-rpg/mlk/rpg/battle.c libmlk-rpg/mlk/rpg/battle.h libmlk-rpg/mlk/rpg/character.c libmlk-rpg/mlk/rpg/character.h libmlk-rpg/mlk/rpg/equipment.c libmlk-rpg/mlk/rpg/equipment.h libmlk-rpg/mlk/rpg/inventory.c libmlk-rpg/mlk/rpg/inventory.h libmlk-rpg/mlk/rpg/item.c libmlk-rpg/mlk/rpg/item.h libmlk-rpg/mlk/rpg/map-file.c libmlk-rpg/mlk/rpg/map-file.h libmlk-rpg/mlk/rpg/map.c libmlk-rpg/mlk/rpg/map.h libmlk-rpg/mlk/rpg/message.c libmlk-rpg/mlk/rpg/message.h libmlk-rpg/mlk/rpg/property.c libmlk-rpg/mlk/rpg/property.h libmlk-rpg/mlk/rpg/quest.c libmlk-rpg/mlk/rpg/quest.h libmlk-rpg/mlk/rpg/rpg.c libmlk-rpg/mlk/rpg/rpg.h libmlk-rpg/mlk/rpg/save.c libmlk-rpg/mlk/rpg/save.h libmlk-rpg/mlk/rpg/selection.c libmlk-rpg/mlk/rpg/selection.h libmlk-rpg/mlk/rpg/spell.c libmlk-rpg/mlk/rpg/spell.h libmlk-rpg/mlk/rpg/team.c libmlk-rpg/mlk/rpg/team.h libmlk-rpg/mlk/rpg/tileset-file.c libmlk-rpg/mlk/rpg/tileset-file.h libmlk-rpg/mlk/rpg/tileset.c libmlk-rpg/mlk/rpg/tileset.h libmlk-rpg/mlk/rpg/walksprite.c libmlk-rpg/mlk/rpg/walksprite.h src/libmlk-rpg/assets/sql/character-load.sql src/libmlk-rpg/assets/sql/character-save.sql src/libmlk-rpg/assets/sql/init.sql src/libmlk-rpg/assets/sql/property-load.sql src/libmlk-rpg/assets/sql/property-remove.sql src/libmlk-rpg/assets/sql/property-save.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/battle-bar-default.c src/libmlk-rpg/rpg/battle-bar-default.h src/libmlk-rpg/rpg/battle-bar.c src/libmlk-rpg/rpg/battle-bar.h src/libmlk-rpg/rpg/battle-entity-state-attacking.c src/libmlk-rpg/rpg/battle-entity-state-attacking.h src/libmlk-rpg/rpg/battle-entity-state-blinking.c src/libmlk-rpg/rpg/battle-entity-state-blinking.h src/libmlk-rpg/rpg/battle-entity-state-moving.c src/libmlk-rpg/rpg/battle-entity-state-moving.h src/libmlk-rpg/rpg/battle-entity-state-normal.c src/libmlk-rpg/rpg/battle-entity-state-normal.h src/libmlk-rpg/rpg/battle-entity-state.c src/libmlk-rpg/rpg/battle-entity-state.h src/libmlk-rpg/rpg/battle-entity.c src/libmlk-rpg/rpg/battle-entity.h src/libmlk-rpg/rpg/battle-indicator.c src/libmlk-rpg/rpg/battle-indicator.h src/libmlk-rpg/rpg/battle-message.c src/libmlk-rpg/rpg/battle-message.h src/libmlk-rpg/rpg/battle-state-ai.c src/libmlk-rpg/rpg/battle-state-ai.h src/libmlk-rpg/rpg/battle-state-attacking.c src/libmlk-rpg/rpg/battle-state-attacking.h src/libmlk-rpg/rpg/battle-state-check.c src/libmlk-rpg/rpg/battle-state-check.h src/libmlk-rpg/rpg/battle-state-closing.c src/libmlk-rpg/rpg/battle-state-closing.h src/libmlk-rpg/rpg/battle-state-item.c src/libmlk-rpg/rpg/battle-state-item.h src/libmlk-rpg/rpg/battle-state-lost.c src/libmlk-rpg/rpg/battle-state-lost.h src/libmlk-rpg/rpg/battle-state-menu.c src/libmlk-rpg/rpg/battle-state-menu.h src/libmlk-rpg/rpg/battle-state-opening.c src/libmlk-rpg/rpg/battle-state-opening.h src/libmlk-rpg/rpg/battle-state-rendering.c src/libmlk-rpg/rpg/battle-state-rendering.h src/libmlk-rpg/rpg/battle-state-selection.c src/libmlk-rpg/rpg/battle-state-selection.h src/libmlk-rpg/rpg/battle-state-victory.c src/libmlk-rpg/rpg/battle-state-victory.h src/libmlk-rpg/rpg/battle-state.c src/libmlk-rpg/rpg/battle-state.h src/libmlk-rpg/rpg/battle.c src/libmlk-rpg/rpg/battle.h src/libmlk-rpg/rpg/character.c src/libmlk-rpg/rpg/character.h src/libmlk-rpg/rpg/equipment.c src/libmlk-rpg/rpg/equipment.h src/libmlk-rpg/rpg/inventory.c src/libmlk-rpg/rpg/inventory.h src/libmlk-rpg/rpg/item.c src/libmlk-rpg/rpg/item.h src/libmlk-rpg/rpg/map-file.c src/libmlk-rpg/rpg/map-file.h src/libmlk-rpg/rpg/map.c src/libmlk-rpg/rpg/map.h src/libmlk-rpg/rpg/message.c src/libmlk-rpg/rpg/message.h src/libmlk-rpg/rpg/property.c src/libmlk-rpg/rpg/property.h src/libmlk-rpg/rpg/quest.c src/libmlk-rpg/rpg/quest.h src/libmlk-rpg/rpg/rpg.c src/libmlk-rpg/rpg/rpg.h src/libmlk-rpg/rpg/save.c src/libmlk-rpg/rpg/save.h src/libmlk-rpg/rpg/selection.c src/libmlk-rpg/rpg/selection.h src/libmlk-rpg/rpg/spell.c src/libmlk-rpg/rpg/spell.h src/libmlk-rpg/rpg/team.c src/libmlk-rpg/rpg/team.h src/libmlk-rpg/rpg/tileset-file.c src/libmlk-rpg/rpg/tileset-file.h src/libmlk-rpg/rpg/tileset.c src/libmlk-rpg/rpg/tileset.h src/libmlk-rpg/rpg/walksprite.c src/libmlk-rpg/rpg/walksprite.h tests/test-character.c tests/test-map.c tests/test-save-quest.c tests/test-save.c tests/test-tileset.c
diffstat 190 files changed, 9071 insertions(+), 9072 deletions(-) [+]
line wrap: on
line diff
--- a/GNUmakefile	Sat Oct 15 21:19:25 2022 +0200
+++ b/GNUmakefile	Sat Oct 15 21:24:17 2022 +0200
@@ -44,7 +44,7 @@
                         -Ilibmlk-util \
                         -Ilibmlk-core \
                         -Ilibmlk-ui \
-                        -Isrc/libmlk-rpg \
+                        -Ilibmlk-rpg \
                         -I. \
                         $(SDL2_INCS) \
                         $(SDL2_IMAGE_INCS) \
@@ -254,59 +254,59 @@
 # {{{ libmlk-rpg
 
 LIBMLK_RPG :=           libmlk-rpg.a
-LIBMLK_RPG_SRCS :=      src/libmlk-rpg/rpg/battle-bar-default.c \
-                        src/libmlk-rpg/rpg/battle-bar.c \
-                        src/libmlk-rpg/rpg/battle-entity-state-attacking.c \
-                        src/libmlk-rpg/rpg/battle-entity-state-blinking.c \
-                        src/libmlk-rpg/rpg/battle-entity-state-moving.c \
-                        src/libmlk-rpg/rpg/battle-entity-state-normal.c \
-                        src/libmlk-rpg/rpg/battle-entity-state.c \
-                        src/libmlk-rpg/rpg/battle-entity.c \
-                        src/libmlk-rpg/rpg/battle-indicator.c \
-                        src/libmlk-rpg/rpg/battle-message.c \
-                        src/libmlk-rpg/rpg/battle-state-ai.c \
-                        src/libmlk-rpg/rpg/battle-state-attacking.c \
-                        src/libmlk-rpg/rpg/battle-state-check.c \
-                        src/libmlk-rpg/rpg/battle-state-closing.c \
-                        src/libmlk-rpg/rpg/battle-state-item.c \
-                        src/libmlk-rpg/rpg/battle-state-lost.c \
-                        src/libmlk-rpg/rpg/battle-state-menu.c \
-                        src/libmlk-rpg/rpg/battle-state-opening.c \
-                        src/libmlk-rpg/rpg/battle-state-rendering.c \
-                        src/libmlk-rpg/rpg/battle-state-selection.c \
-                        src/libmlk-rpg/rpg/battle-state-victory.c \
-                        src/libmlk-rpg/rpg/battle-state.c \
-                        src/libmlk-rpg/rpg/battle.c \
-                        src/libmlk-rpg/rpg/character.c \
-                        src/libmlk-rpg/rpg/equipment.c \
-                        src/libmlk-rpg/rpg/inventory.c \
-                        src/libmlk-rpg/rpg/item.c \
-                        src/libmlk-rpg/rpg/map-file.c \
-                        src/libmlk-rpg/rpg/map.c \
-                        src/libmlk-rpg/rpg/message.c \
-                        src/libmlk-rpg/rpg/property.c \
-                        src/libmlk-rpg/rpg/quest.c \
-                        src/libmlk-rpg/rpg/rpg.c \
-                        src/libmlk-rpg/rpg/save.c \
-                        src/libmlk-rpg/rpg/selection.c \
-                        src/libmlk-rpg/rpg/spell.c \
-                        src/libmlk-rpg/rpg/team.c \
-                        src/libmlk-rpg/rpg/tileset-file.c \
-                        src/libmlk-rpg/rpg/tileset.c \
-                        src/libmlk-rpg/rpg/walksprite.c
+LIBMLK_RPG_SRCS :=      libmlk-rpg/mlk/rpg/battle-bar-default.c \
+                        libmlk-rpg/mlk/rpg/battle-bar.c \
+                        libmlk-rpg/mlk/rpg/battle-entity-state-attacking.c \
+                        libmlk-rpg/mlk/rpg/battle-entity-state-blinking.c \
+                        libmlk-rpg/mlk/rpg/battle-entity-state-moving.c \
+                        libmlk-rpg/mlk/rpg/battle-entity-state-normal.c \
+                        libmlk-rpg/mlk/rpg/battle-entity-state.c \
+                        libmlk-rpg/mlk/rpg/battle-entity.c \
+                        libmlk-rpg/mlk/rpg/battle-indicator.c \
+                        libmlk-rpg/mlk/rpg/battle-message.c \
+                        libmlk-rpg/mlk/rpg/battle-state-ai.c \
+                        libmlk-rpg/mlk/rpg/battle-state-attacking.c \
+                        libmlk-rpg/mlk/rpg/battle-state-check.c \
+                        libmlk-rpg/mlk/rpg/battle-state-closing.c \
+                        libmlk-rpg/mlk/rpg/battle-state-item.c \
+                        libmlk-rpg/mlk/rpg/battle-state-lost.c \
+                        libmlk-rpg/mlk/rpg/battle-state-menu.c \
+                        libmlk-rpg/mlk/rpg/battle-state-opening.c \
+                        libmlk-rpg/mlk/rpg/battle-state-rendering.c \
+                        libmlk-rpg/mlk/rpg/battle-state-selection.c \
+                        libmlk-rpg/mlk/rpg/battle-state-victory.c \
+                        libmlk-rpg/mlk/rpg/battle-state.c \
+                        libmlk-rpg/mlk/rpg/battle.c \
+                        libmlk-rpg/mlk/rpg/character.c \
+                        libmlk-rpg/mlk/rpg/equipment.c \
+                        libmlk-rpg/mlk/rpg/inventory.c \
+                        libmlk-rpg/mlk/rpg/item.c \
+                        libmlk-rpg/mlk/rpg/map-file.c \
+                        libmlk-rpg/mlk/rpg/map.c \
+                        libmlk-rpg/mlk/rpg/message.c \
+                        libmlk-rpg/mlk/rpg/property.c \
+                        libmlk-rpg/mlk/rpg/quest.c \
+                        libmlk-rpg/mlk/rpg/rpg.c \
+                        libmlk-rpg/mlk/rpg/save.c \
+                        libmlk-rpg/mlk/rpg/selection.c \
+                        libmlk-rpg/mlk/rpg/spell.c \
+                        libmlk-rpg/mlk/rpg/team.c \
+                        libmlk-rpg/mlk/rpg/tileset-file.c \
+                        libmlk-rpg/mlk/rpg/tileset.c \
+                        libmlk-rpg/mlk/rpg/walksprite.c
 LIBMLK_RPG_OBJS :=      $(LIBMLK_RPG_SRCS:.c=.o)
 LIBMLK_RPG_DEPS :=      $(LIBMLK_RPG_SRCS:.c=.d)
 
-LIBMLK_RPG_DATA_SRCS := src/libmlk-rpg/assets/sql/character-load.sql \
-                        src/libmlk-rpg/assets/sql/character-save.sql \
-                        src/libmlk-rpg/assets/sql/init.sql \
-                        src/libmlk-rpg/assets/sql/property-load.sql \
-                        src/libmlk-rpg/assets/sql/property-remove.sql \
-                        src/libmlk-rpg/assets/sql/property-save.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
+LIBMLK_RPG_DATA_SRCS := libmlk-rpg/assets/sql/character-load.sql \
+                        libmlk-rpg/assets/sql/character-save.sql \
+                        libmlk-rpg/assets/sql/init.sql \
+                        libmlk-rpg/assets/sql/property-load.sql \
+                        libmlk-rpg/assets/sql/property-remove.sql \
+                        libmlk-rpg/assets/sql/property-save.sql \
+                        libmlk-rpg/assets/sql/quest-remove.sql \
+                        libmlk-rpg/assets/sql/quest-save.sql \
+                        libmlk-rpg/assets/sql/quest-step-load.sql \
+                        libmlk-rpg/assets/sql/quest-step-save.sql
 LIBMLK_RPG_DATA_OBJS := $(addsuffix .h,$(basename $(LIBMLK_RPG_DATA_SRCS)))
 
 $(LIBMLK_RPG_DATA_OBJS): BCC_OPTS := -cs0
--- a/examples/example-action/example-action.c	Sat Oct 15 21:19:25 2022 +0200
+++ b/examples/example-action/example-action.c	Sat Oct 15 21:24:17 2022 +0200
@@ -38,8 +38,8 @@
 #include <mlk/ui/theme.h>
 #include <mlk/ui/ui.h>
 
-#include <rpg/message.h>
-#include <rpg/rpg.h>
+#include <mlk/rpg/message.h>
+#include <mlk/rpg/rpg.h>
 
 #include <assets/sprites/chest.h>
 #include <assets/sprites/people.h>
--- a/examples/example-message/example-message.c	Sat Oct 15 21:19:25 2022 +0200
+++ b/examples/example-message/example-message.c	Sat Oct 15 21:24:17 2022 +0200
@@ -32,8 +32,8 @@
 #include <mlk/ui/theme.h>
 #include <mlk/ui/ui.h>
 
-#include <rpg/message.h>
-#include <rpg/rpg.h>
+#include <mlk/rpg/message.h>
+#include <mlk/rpg/rpg.h>
 
 #define W       (1280)
 #define H       (720)
--- a/examples/example/character-john.c	Sat Oct 15 21:19:25 2022 +0200
+++ b/examples/example/character-john.c	Sat Oct 15 21:24:17 2022 +0200
@@ -16,7 +16,7 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-#include <rpg/character.h>
+#include <mlk/rpg/character.h>
 
 #include "character-john.h"
 #include "spell-fire.h"
--- a/examples/example/spell-fire.c	Sat Oct 15 21:19:25 2022 +0200
+++ b/examples/example/spell-fire.c	Sat Oct 15 21:24:17 2022 +0200
@@ -25,11 +25,11 @@
 
 #include <mlk/ui/align.h>
 
-#include <rpg/battle-state-check.h>
-#include <rpg/battle-state-rendering.h>
-#include <rpg/battle.h>
-#include <rpg/character.h>
-#include <rpg/spell.h>
+#include <mlk/rpg/battle-state-check.h>
+#include <mlk/rpg/battle-state-rendering.h>
+#include <mlk/rpg/battle.h>
+#include <mlk/rpg/character.h>
+#include <mlk/rpg/spell.h>
 
 #include "registry.h"
 #include "spell-fire.h"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-rpg/assets/sql/character-load.sql	Sat Oct 15 21:24:17 2022 +0200
@@ -0,0 +1,30 @@
+--
+-- character-load.sql -- load a character data
+--
+-- Copyright (c) 2020-2022 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	Sat Oct 15 21:24:17 2022 +0200
@@ -0,0 +1,44 @@
+--
+-- character-save.sql -- save or replace character
+--
+-- Copyright (c) 2020-2022 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	Sat Oct 15 21:24:17 2022 +0200
@@ -0,0 +1,55 @@
+--
+-- init.sql -- initialize database
+--
+-- Copyright (c) 2020-2022 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
+);
+
+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'));
+
+COMMIT;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-rpg/assets/sql/property-load.sql	Sat Oct 15 21:24:17 2022 +0200
@@ -0,0 +1,21 @@
+--
+-- property-load.sql -- get a property
+--
+-- Copyright (c) 2020-2022 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	Sat Oct 15 21:24:17 2022 +0200
@@ -0,0 +1,21 @@
+--
+-- property-remove.sql -- remove a property
+--
+-- Copyright (c) 2020-2022 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-save.sql	Sat Oct 15 21:24:17 2022 +0200
@@ -0,0 +1,26 @@
+--
+-- property-save.sql -- set a property
+--
+-- Copyright (c) 2020-2022 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(
+	?,
+	?
+)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-rpg/assets/sql/quest-remove.sql	Sat Oct 15 21:24:17 2022 +0200
@@ -0,0 +1,21 @@
+--
+-- quest-remove.sql -- remove a quest entirely
+--
+-- Copyright (c) 2020-2022 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/libmlk-rpg/assets/sql/quest-save.sql	Sat Oct 15 21:24:17 2022 +0200
@@ -0,0 +1,24 @@
+--
+-- quest-save.sql -- create parent quest entry
+--
+-- Copyright (c) 2020-2022 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/libmlk-rpg/assets/sql/quest-step-load.sql	Sat Oct 15 21:24:17 2022 +0200
@@ -0,0 +1,21 @@
+--
+-- quest-step-load.sql -- remove a quest entirely
+--
+-- Copyright (c) 2020-2022 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/libmlk-rpg/assets/sql/quest-step-save.sql	Sat Oct 15 21:24:17 2022 +0200
@@ -0,0 +1,27 @@
+--
+-- quest-step-save.sql -- save a quest step
+--
+-- Copyright (c) 2020-2022 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/libmlk-rpg/mlk/rpg/battle-bar-default.c	Sat Oct 15 21:24:17 2022 +0200
@@ -0,0 +1,693 @@
+/*
+ * battle-bar-default.c -- default battle status bar and menu implementation
+ *
+ * Copyright (c) 2020-2022 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 <stdlib.h>
+#include <string.h>
+
+#include <mlk/core/alloc.h>
+#include <mlk/core/event.h>
+#include <mlk/core/font.h>
+#include <mlk/core/sprite.h>
+#include <mlk/core/trace.h>
+#include <mlk/core/util.h>
+#include <mlk/core/window.h>
+
+#include <mlk/ui/align.h>
+#include <mlk/ui/theme.h>
+
+#include "battle-bar-default.h"
+#include "battle-bar.h"
+#include "battle-state-item.h"
+#include "battle-state-selection.h"
+#include "battle.h"
+#include "character.h"
+#include "inventory.h"
+#include "item.h"
+#include "spell.h"
+
+#define THEME(bar) ((bar)->theme ? (bar)->theme : theme_default())
+
+struct geo {
+	int x, y;
+	unsigned int w, h;
+};
+
+static inline void
+dimensions(struct geo geo[2], const struct battle_bar_default *bar)
+{
+	/* 0 == main menu */
+	geo[0].w = bar->w * 0.2;
+	geo[0].h = bar->h;
+	geo[0].x = bar->x + (bar->w / 2) - (geo[0].w / 2);
+	geo[0].y = window.h - bar->h;
+
+	/* 1 == status frame */
+	geo[1].x = geo[0].x + geo[0].w;
+	geo[1].y = geo[0].y;
+	geo[1].w = (bar->w - geo[0].w) / 2;
+	geo[1].h = bar->h;
+}
+
+/*
+ * The following validate_* functions are called when the user has validated a
+ * selection depending on the current menu (e.g. Magic, Items).
+ *
+ * They change the battle state to the appropriate one.
+ */
+
+static void
+validate_attack(struct battle_bar_default *bar, struct battle *bt, const struct selection *sel)
+{
+	(void)bar;
+
+	struct character *target;
+
+	if (sel->index_side == 0)
+		target = bt->enemies[sel->index_character]->ch;
+	else
+		target = bt->team[sel->index_character]->ch;
+
+	battle_attack(bt, battle_current(bt)->ch, target);
+}
+
+static void
+validate_magic(struct battle_bar_default *bar, struct battle *bt, const struct selection *sel)
+{
+	struct character *source = battle_current(bt)->ch;
+	const struct spell *spell = source->spells[bar->grid.selected];
+
+	battle_cast(bt, source, spell, sel);
+}
+
+static void
+validate_item(struct battle_bar_default *bar, struct battle *bt, const struct selection *sel)
+{
+	struct inventory_slot *slot;
+	struct battle_entity *source, *target;
+
+	if (bar->grid.selected >= INVENTORY_ITEM_MAX)
+		return;
+	if (!(slot = &bt->inventory->items[bar->grid.selected]))
+		return;
+
+	source = battle_current(bt);
+	target = sel->index_side == 0
+		? bt->enemies[sel->index_character]
+		: bt->team[sel->index_character];
+
+	/* TODO: battle_use? */
+	battle_state_item(bt, source, target, slot);
+}
+
+/*
+ * The following functions are used to switch to the battle selection state
+ * using the appropriate selector algorithm. For example, an item can only be
+ * used on a unique target while a spell can have multiple choices.
+ */
+
+static void
+switch_selection_attack(struct battle_bar_default *bar, struct battle *bt)
+{
+	struct selection sel = {
+		.allowed_kinds = SELECTION_KIND_ONE,
+		.allowed_sides = SELECTION_SIDE_ENEMY,
+		.index_side = 0
+	};
+
+	/* Disable handling anymore. */
+	bar->state = BATTLE_BAR_DEFAULT_STATE_NONE;
+
+	selection_first(&sel, bt);
+	battle_state_selection(bt, &sel);
+}
+
+static void
+switch_selection_spell(struct battle_bar_default *bar, struct battle *bt)
+{
+	const struct character *ch = battle_current(bt)->ch;
+	const struct spell *sp = ch->spells[bar->grid.selected];
+	struct selection sel = {0};
+
+	if (bar->grid.selected > CHARACTER_SPELL_MAX)
+		return;
+	if (!(sp = ch->spells[bar->grid.selected]) || sp->mp > (unsigned int)ch->mp)
+		return;
+
+	/* Use the spell selection algorithm to fill default values. */
+	spell_select(sp, bt, &sel);
+	battle_state_selection(bt, &sel);
+
+	/* A cursor should be present. */
+	if (!sprite_ok(BATTLE_THEME(bt)->sprites[THEME_SPRITE_CURSOR]))
+		tracef("battle: no cursor sprite in theme");
+}
+
+static void
+switch_selection_item(struct battle *bt)
+{
+	const struct selection slt = {
+		.allowed_kinds = SELECTION_KIND_ONE,
+		.allowed_sides = SELECTION_SIDE_TEAM | SELECTION_SIDE_ENEMY,
+		.index_side = 1,
+		.index_character = battle_index(bt)
+	};
+
+	battle_state_selection(bt, &slt);
+}
+
+/*
+ * The following functions actually draw the bar and their components depending
+ * on the current selected menu.
+ */
+
+static void
+draw_help(const struct battle_bar_default *bar, const char *what)
+{
+	struct label label = {0};
+	unsigned int lw = 0, lh = 0;
+
+	label.flags = LABEL_FLAGS_SHADOW;
+	label.text = what;
+	label_query(&label, &lw, &lh);
+	label.x = bar->grid.x + (bar->grid.w / 2) - (lw / 2);
+	label.y = bar->grid.y - lh - THEME(bar)->padding;
+	label_draw(&label);
+}
+
+static void
+draw_spell_help(const struct battle_bar_default *bar, const struct battle *bt)
+{
+	const struct character *ch = battle_current(bt)->ch;
+	const struct spell *sp;
+
+	if (bar->grid.selected >= CHARACTER_SPELL_MAX)
+		return;
+	if (!(sp = ch->spells[bar->grid.selected]))
+		return;
+
+	draw_help(bar, sp->description);
+}
+
+static void
+draw_item_help(const struct battle_bar_default *bar, const struct battle *bt)
+{
+	const struct inventory_slot *slot;
+
+	if (bar->grid.selected >= INVENTORY_ITEM_MAX)
+		return;
+
+	slot = &bt->inventory->items[bar->grid.selected];
+
+	if (!slot->item)
+		return;
+
+	draw_help(bar, slot->item->description);
+}
+
+static void
+draw_status_character_stats(const struct battle_bar_default *bar,
+                            const struct character *ch,
+                            int x,
+                            int y,
+                            unsigned int h)
+{
+	const struct theme *theme = THEME(bar);
+	struct label label;
+	unsigned int spacing, lw, lh;
+	char line[64];
+
+	/* Compute spacing between elements. */
+	spacing = h - (font_height(theme->fonts[THEME_FONT_INTERFACE]) * 3);
+	spacing /= 4;
+
+	/* Reuse the same label. */
+	label.theme = theme;
+	label.text = line;
+	label.flags = LABEL_FLAGS_SHADOW;
+
+	/* Name. */
+	snprintf(line, sizeof (line), "%s", ch->name);
+	label_query(&label, &lw, &lh);
+	label.x = x + theme->padding;
+	label.y = y + spacing;
+	label_draw(&label);
+
+	/* HP. */
+	snprintf(line, sizeof (line), "%d/%u", ch->hp, ch->hpmax);
+	label_query(&label, &lw, &lh);
+	label.x = x + theme->padding;
+	label.y = label.y + lh + spacing;
+	label_draw(&label);
+
+	/* MP. */
+	snprintf(line, sizeof (line), "%d/%u", ch->mp, ch->mpmax);
+	label_query(&label, &lw, &lh);
+	label.x = x + theme->padding;
+	label.y = label.y + lh + spacing;
+	label_draw(&label);
+
+	/* Status. */
+	/* TODO: list all status. */
+}
+
+static void
+draw_status_character(const struct battle_bar_default *bar,
+                      const struct battle *bt,
+                      const struct character *ch,
+                      const struct geo *geo,
+                      unsigned int index)
+{
+	int x, y;
+	unsigned int w, h;
+
+	/* Compute bounding box for rendering. */
+	w = geo->w / bt->teamsz;
+	h = geo->h;
+	x = geo->x + (index * w);
+	y = geo->y;
+
+	draw_status_character_stats(bar, ch, x, y, h);
+}
+
+static void
+draw_status_characters(const struct battle_bar_default *bar,
+                       const struct battle *bt,
+                       const struct geo *geo)
+{
+	const struct battle_entity *et;
+	unsigned int index = 0;
+
+	BATTLE_TEAM_FOREACH(bt, et) {
+		if (character_ok(et->ch))
+			draw_status_character(bar, bt, et->ch, geo, index);
+
+		++index;
+	}
+}
+
+static void
+draw_status(const struct battle_bar_default *bar, const struct battle *bt, const struct geo *geo)
+{
+	frame_draw(&(const struct frame) {
+		.x = geo->x,
+		.y = geo->y,
+		.w = geo->w,
+		.h = geo->h
+	});
+
+	draw_status_characters(bar, bt, geo);
+}
+
+static void
+draw_menu(const struct battle_bar_default *bar, const struct geo *geo)
+{
+	struct {
+		unsigned int w, h;
+		enum align align;
+		struct label label;
+	} buttons[] = {
+		{
+			.align = ALIGN_TOP,
+			.label = {
+				.text = "Attack",
+				.flags = LABEL_FLAGS_SHADOW
+			}
+		},
+		{
+			.align = ALIGN_RIGHT,
+			.label = {
+				.text = "Magic",
+				.flags = LABEL_FLAGS_SHADOW
+			}
+		},
+		{
+			.align = ALIGN_BOTTOM,
+			.label = {
+				.text = "Objects",
+				.flags = LABEL_FLAGS_SHADOW
+			}
+		},
+		{
+			.align = ALIGN_LEFT,
+			.label = {
+				.text = "Special",
+				.flags = LABEL_FLAGS_SHADOW
+			}
+		}
+	};
+
+	const struct theme *theme = THEME(bar);
+	int bx, by;
+	unsigned int bw, bh;
+
+	/* Compute bounding box with margins removed. */
+	bx = geo->x + theme->padding;
+	by = geo->y + theme->padding;
+	bw = geo->w - theme->padding * 2;
+	bh = geo->h - theme->padding * 2;
+
+	/* Draw menu frame. */
+	frame_draw(&(const struct frame) {
+		.x = geo->x,
+		.y = geo->y,
+		.w = geo->w,
+		.h = geo->h
+	});
+
+	for (size_t i = 0; i < UTIL_SIZE(buttons); ++i) {
+		buttons[i].label.theme = theme;
+
+		label_query(&buttons[i].label, &buttons[i].w, &buttons[i].h);
+
+		/* Change theme if it's selected. */
+		if ((size_t)bar->menu == i)
+			buttons[i].label.flags |=  LABEL_FLAGS_SELECTED;
+		else
+			buttons[i].label.flags &= ~LABEL_FLAGS_SELECTED;
+
+		align(buttons[i].align,
+		    &buttons[i].label.x, &buttons[i].label.y, buttons[i].w, buttons[i].h,
+		    bx, by, bw, bh);
+		label_draw(&buttons[i].label);
+	}
+}
+
+/*
+ * This function is called only in the first level of the bar menu: selecting
+ * one of the Attack, Magic, Item and Special items.
+ */
+static void
+handle_keydown_menu(struct battle_bar_default *bar, struct battle *bt, const union event *ev)
+{
+	(void)bt;
+
+	switch (ev->key.key) {
+	case KEY_UP:
+		bar->menu = BATTLE_BAR_DEFAULT_MENU_ATTACK;
+		break;
+	case KEY_RIGHT:
+		bar->menu = BATTLE_BAR_DEFAULT_MENU_MAGIC;
+		break;
+	case KEY_DOWN:
+		bar->menu = BATTLE_BAR_DEFAULT_MENU_ITEM;
+		break;
+	case KEY_LEFT:
+		bar->menu = BATTLE_BAR_DEFAULT_MENU_SPECIAL;
+		break;
+	case KEY_ENTER:
+		/*
+		 * At this step, attack does not require opening the sub menu so
+		 * we change selection state immediately if needed.
+		 */
+		switch (bar->menu) {
+		case BATTLE_BAR_DEFAULT_MENU_ATTACK:
+			switch_selection_attack(bar, bt);
+			break;
+		case BATTLE_BAR_DEFAULT_MENU_ITEM:
+			battle_bar_default_open_item(bar, bt);
+			break;
+		case BATTLE_BAR_DEFAULT_MENU_MAGIC:
+			battle_bar_default_open_magic(bar, bt, battle_current(bt)->ch);
+			break;
+		default:
+			break;
+		}
+		break;
+	default:
+		break;
+	}
+}
+
+/*
+ * This function is called when we're selecting a submenu entry from Items
+ * and Magic.
+ */
+static void
+handle_keydown_grid(struct battle_bar_default *bar, struct battle *bt, const union event *ev)
+{
+	/* Go back to main menu if I press escape. */
+	if (ev->key.key == KEY_ESCAPE)
+		bar->state = BATTLE_BAR_DEFAULT_STATE_MENU;
+	else if (gridmenu_handle(&bar->grid, ev)) {
+		switch (bar->menu) {
+		case BATTLE_BAR_DEFAULT_MENU_MAGIC:
+			switch_selection_spell(bar, bt);
+			break;
+		case BATTLE_BAR_DEFAULT_MENU_ITEM:
+			switch_selection_item(bt);
+			break;
+		default:
+			break;
+		}
+	}
+}
+
+static void
+handle_keydown(struct battle_bar_default *bar, struct battle *bt, const union event *ev)
+{
+	assert(ev->type == EVENT_KEYDOWN);
+
+	static void (*handlers[])(struct battle_bar_default *, struct battle *, const union event *) = {
+		[BATTLE_BAR_DEFAULT_STATE_MENU] = handle_keydown_menu,
+		[BATTLE_BAR_DEFAULT_STATE_GRID] = handle_keydown_grid
+	};
+
+	if (handlers[bar->state])
+		handlers[bar->state](bar, bt, ev);
+}
+
+#if 0
+
+static void
+handle_clickdown(struct battle_bar_default *bar, struct battle *bt, const union event *ev)
+{
+	assert(ev->type == EVENT_CLICKDOWN);
+
+	(void)bar;
+	(void)bt;
+
+	switch (bar->state) {
+	case BATTLE_BAR_DEFAULT_STATE_MENU:
+		/* We are selecting a main menu entry. */
+		/* TODO: implement click here too. */
+		break;
+	case BATTLE_BAR_DEFAULT_STATE_SUB:
+		/* We are in the sub menu (objects/spells). */
+		if (bar->sub_grid.state == GRIDMENU_STATE_ACTIVATED)
+	default:
+		break;
+	}
+
+	return 0;
+}
+
+#endif
+
+static void
+self_start(struct battle_bar *bar, struct battle *bt)
+{
+	(void)bt;
+
+	battle_bar_default_start(bar->data);
+}
+
+static void
+self_select(struct battle_bar *bar, struct battle *bt, const struct selection *sel)
+{
+	battle_bar_default_select(bar->data, bt, sel);
+}
+
+static void
+self_handle(struct battle_bar *bar, struct battle *bt, const union event *ev)
+{
+	battle_bar_default_handle(bar->data, bt, ev);
+}
+
+static void
+self_draw(const struct battle_bar *bar, const struct battle *bt)
+{
+	battle_bar_default_draw(bar->data, bt);
+}
+
+void
+battle_bar_default_init(struct battle_bar_default *bar)
+{
+	assert(bar);
+
+	struct geo geo[2];
+
+	memset(bar, 0, sizeof (*bar));
+
+	bar->w = window.w;
+	bar->h = window.h * 0.12;
+	bar->x = 0;
+	bar->y = window.h - bar->h;
+
+	dimensions(geo, bar);
+
+	gridmenu_init(&bar->grid, 2, 2, NULL, 0);
+	gridmenu_resize(&bar->grid, bar->x, geo[0].y, geo[1].w, bar->h);
+	bar->grid.theme = bar->theme;
+}
+
+void
+battle_bar_default_open_magic(struct battle_bar_default *bar, const struct battle *bt, struct character *ch)
+{
+	assert(bar);
+	assert(bt);
+	assert(ch);
+
+	(void)bt;
+
+	bar->items = alloc_rearray0(bar->items, bar->itemsz,
+	    CHARACTER_SPELL_MAX, sizeof (*bar->items));
+	bar->itemsz = CHARACTER_SPELL_MAX;
+	bar->state = BATTLE_BAR_DEFAULT_STATE_GRID;
+
+	for (size_t i = 0; i < CHARACTER_SPELL_MAX; ++i)
+		if (ch->spells[i])
+			bar->items[i] = ch->spells[i]->name;
+
+	bar->grid.items = bar->items;
+	bar->grid.itemsz = bar->itemsz;
+}
+
+void
+battle_bar_default_open_item(struct battle_bar_default *bar, const struct battle *bt)
+{
+	assert(bar);
+	assert(bt);
+
+	/* TODO: not implemented yet. */
+	(void)bar;
+	(void)bt;
+#if 0
+	for (size_t i = 0; i < INVENTORY_ITEM_MAX; ++i) {
+		if (bt->inventory->items[i].item) {
+			snprintf(bar->sub_items[i], sizeof (bar->sub_items[i]), "%-16s %u",
+			    bt->inventory->items[i].item->name, bt->inventory->items[i].amount);
+			bar->sub_grid.menu[i] = bar->sub_items[i];
+		}
+	}
+
+	bar->state = BATTLE_BAR_DEFAULT_STATE_GRID;
+#endif
+}
+
+void
+battle_bar_default_start(struct battle_bar_default *bar)
+{
+	assert(bar);
+
+	bar->menu = BATTLE_BAR_DEFAULT_MENU_ATTACK;
+	bar->state = BATTLE_BAR_DEFAULT_STATE_MENU;
+}
+
+/*
+ * Apply the battle selection for the current menu item. This function is called
+ * from the battle-state-selection state when the user validated the selection.
+ */
+void
+battle_bar_default_select(struct battle_bar_default *bar, struct battle *bt, const struct selection *sel)
+{
+	assert(bar);
+	assert(bt);
+	assert(sel);
+
+	static void (*validate[])(struct battle_bar_default *, struct battle *, const struct selection *) = {
+		[BATTLE_BAR_DEFAULT_MENU_ATTACK]        = validate_attack,
+		[BATTLE_BAR_DEFAULT_MENU_ITEM]          = validate_item,
+		[BATTLE_BAR_DEFAULT_MENU_MAGIC]         = validate_magic,
+		[BATTLE_BAR_DEFAULT_MENU_SPECIAL]       = NULL
+	};
+
+	if (validate[bar->menu])
+		validate[bar->menu](bar, bt, sel);
+}
+
+void
+battle_bar_default_handle(struct battle_bar_default *bar, struct battle *bt, const union event *ev)
+{
+	assert(bar);
+	assert(bt);
+	assert(ev);
+
+	static void (*handlers[])(struct battle_bar_default *, struct battle *, const union event *) = {
+		[EVENT_KEYDOWN] = handle_keydown,
+		[EVENT_NUM] = NULL
+	};
+
+	if (handlers[ev->type])
+		handlers[ev->type](bar, bt, ev);
+}
+
+void
+battle_bar_default_draw(const struct battle_bar_default *bar, const struct battle *bt)
+{
+	assert(bar);
+	assert(bt);
+
+	struct geo geo[2];
+
+	dimensions(geo, bar);
+	draw_menu(bar, &geo[0]);
+	draw_status(bar, bt, &geo[1]);
+
+	if (bar->state == BATTLE_BAR_DEFAULT_STATE_GRID) {
+		switch (bar->menu) {
+		case BATTLE_BAR_DEFAULT_MENU_MAGIC:
+			draw_spell_help(bar, bt);
+			break;
+		case BATTLE_BAR_DEFAULT_MENU_ITEM:
+			draw_item_help(bar, bt);
+			break;
+		default:
+			break;
+		}
+	}
+
+	/* Sub menu is only shown if state is set to it. */
+	if (bar->state == BATTLE_BAR_DEFAULT_STATE_GRID)
+		gridmenu_draw(&bar->grid);
+}
+
+void
+battle_bar_default_finish(struct battle_bar_default *bar)
+{
+	assert(bar);
+
+	free(bar->items);
+	memset(bar, 0, sizeof (*bar));
+}
+
+void
+battle_bar_default(struct battle_bar_default *self, struct battle_bar *bar)
+{
+	assert(self);
+	assert(bar);
+
+	memset(bar, 0, sizeof (*bar));
+
+	bar->data = self;
+	bar->start = self_start;
+	bar->select = self_select;
+	bar->handle = self_handle;
+	bar->draw = self_draw;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-rpg/mlk/rpg/battle-bar-default.h	Sat Oct 15 21:24:17 2022 +0200
@@ -0,0 +1,93 @@
+/*
+ * battle-bar-default.h -- default battle status bar and menu implementation
+ *
+ * Copyright (c) 2020-2022 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 MLK_RPG_BATTLE_BAR_DEFAULT_H
+#define MLK_RPG_BATTLE_BAR_DEFAULT_H
+
+#include <mlk/core/core.h>
+
+#include <mlk/ui/gridmenu.h>
+
+struct battle;
+struct battle_bar;
+struct character;
+struct selection;
+struct theme;
+
+union event;
+
+enum battle_bar_default_menu {
+	BATTLE_BAR_DEFAULT_MENU_ATTACK,
+	BATTLE_BAR_DEFAULT_MENU_MAGIC,
+	BATTLE_BAR_DEFAULT_MENU_ITEM,
+	BATTLE_BAR_DEFAULT_MENU_SPECIAL
+};
+
+enum battle_bar_default_state {
+	BATTLE_BAR_DEFAULT_STATE_NONE,
+	BATTLE_BAR_DEFAULT_STATE_MENU,
+	BATTLE_BAR_DEFAULT_STATE_GRID
+};
+
+struct battle_bar_default {
+	int x;
+	int y;
+	unsigned int w;
+	unsigned int h;
+	struct theme *theme;
+	enum battle_bar_default_state state;
+	enum battle_bar_default_menu menu;
+
+	/* Private fields. */
+	const char **items;
+	size_t itemsz;
+	struct gridmenu grid;
+};
+
+CORE_BEGIN_DECLS
+
+void
+battle_bar_default_init(struct battle_bar_default *);
+
+void
+battle_bar_default_open_magic(struct battle_bar_default *, const struct battle *, struct character *);
+
+void
+battle_bar_default_open_item(struct battle_bar_default *, const struct battle *);
+
+void
+battle_bar_default_start(struct battle_bar_default *);
+
+void
+battle_bar_default_select(struct battle_bar_default *, struct battle *, const struct selection *);
+
+void
+battle_bar_default_handle(struct battle_bar_default *, struct battle *, const union event *);
+
+void
+battle_bar_default_draw(const struct battle_bar_default *, const struct battle *);
+
+void
+battle_bar_default_finish(struct battle_bar_default *);
+
+void
+battle_bar_default(struct battle_bar_default *, struct battle_bar *);
+
+CORE_END_DECLS
+
+#endif /* !MLK_RPG_BATTLE_BAR_DEFAULT_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-rpg/mlk/rpg/battle-bar.c	Sat Oct 15 21:24:17 2022 +0200
@@ -0,0 +1,84 @@
+/*
+ * battle-bar.c -- abstract battle bar
+ *
+ * Copyright (c) 2020-2022 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 "battle-bar.h"
+
+void
+battle_bar_start(struct battle_bar *bar, struct battle *bt)
+{
+	assert(bar);
+	assert(bt);
+
+	if (bar->start)
+		bar->start(bar, bt);
+}
+
+void
+battle_bar_select(struct battle_bar *bar, struct battle *bt, const struct selection *sel)
+{
+	assert(bar);
+	assert(bt);
+	assert(sel);
+
+	if (bar->select)
+		bar->select(bar, bt, sel);
+
+}
+
+void
+battle_bar_handle(struct battle_bar *bar, struct battle *bt, const union event *ev)
+{
+	assert(bar);
+	assert(bt);
+	assert(ev);
+
+	if (bar->handle)
+		bar->handle(bar, bt, ev);
+}
+
+void
+battle_bar_update(struct battle_bar *bar, struct battle *bt, unsigned int ticks)
+{
+	assert(bar);
+	assert(bt);
+
+	if (bar->update)
+		bar->update(bar, bt, ticks);
+}
+
+void
+battle_bar_draw(const struct battle_bar *bar, const struct battle *bt)
+{
+	assert(bar);
+	assert(bt);
+
+	if (bar->draw)
+		bar->draw(bar, bt);
+}
+
+void
+battle_bar_finish(struct battle_bar *bar, struct battle *bt)
+{
+	assert(bar);
+	assert(bt);
+
+	if (bar->finish)
+		bar->finish(bar, bt);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-rpg/mlk/rpg/battle-bar.h	Sat Oct 15 21:24:17 2022 +0200
@@ -0,0 +1,61 @@
+/*
+ * battle-bar.h -- abstract battle bar
+ *
+ * Copyright (c) 2020-2022 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 MLK_RPG_BATTLE_BAR_H
+#define MLK_RPG_BATTLE_BAR_H
+
+#include <mlk/core/core.h>
+
+struct battle;
+struct selection;
+
+union event;
+
+struct battle_bar {
+	void *data;
+	void (*start)(struct battle_bar *, struct battle *);
+	void (*select)(struct battle_bar *, struct battle *, const struct selection *);
+	void (*handle)(struct battle_bar *, struct battle *, const union event *);
+	void (*update)(struct battle_bar *, struct battle *, unsigned int);
+	void (*draw)(const struct battle_bar *, const struct battle *);
+	void (*finish)(struct battle_bar *, struct battle *);
+};
+
+CORE_BEGIN_DECLS
+
+void
+battle_bar_start(struct battle_bar *, struct battle *);
+
+void
+battle_bar_select(struct battle_bar *, struct battle *, const struct selection *);
+
+void
+battle_bar_handle(struct battle_bar *, struct battle *, const union event *);
+
+void
+battle_bar_update(struct battle_bar *, struct battle *, unsigned int);
+
+void
+battle_bar_draw(const struct battle_bar *, const struct battle *);
+
+void
+battle_bar_finish(struct battle_bar *, struct battle *);
+
+CORE_END_DECLS
+
+#endif /* !MLK_RPG_BATTLE_BAR_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-rpg/mlk/rpg/battle-entity-state-attacking.c	Sat Oct 15 21:24:17 2022 +0200
@@ -0,0 +1,101 @@
+/*
+ * battle-entity-state-attacking.h -- the entity is attacking
+ *
+ * Copyright (c) 2020-2022 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 <stdlib.h>
+
+#include <mlk/core/alloc.h>
+#include <mlk/core/animation.h>
+#include <mlk/core/panic.h>
+#include <mlk/core/sprite.h>
+
+#include "battle-entity-state-attacking.h"
+#include "battle-entity-state.h"
+#include "battle-entity.h"
+
+struct self {
+	struct battle_entity_state_attacking data;
+	struct battle_entity_state state;
+};
+
+static int
+update(struct battle_entity_state *st, struct battle_entity *et, unsigned int ticks)
+{
+	(void)et;
+
+	return battle_entity_state_attacking_update(st->data, ticks);
+}
+
+static void
+draw(const struct battle_entity_state *st, const struct battle_entity *et)
+{
+	battle_entity_state_attacking_draw(st->data, et);
+}
+
+static void
+finish(struct battle_entity_state *st, struct battle_entity *et)
+{
+	(void)et;
+
+	free(st->data);
+}
+
+void
+battle_entity_state_attacking_init(struct battle_entity_state_attacking *atk, const struct sprite *which)
+{
+	assert(atk);
+	assert(sprite_ok(which));
+
+	animation_init(&atk->anim, which, 100);
+	animation_start(&atk->anim);
+}
+
+int
+battle_entity_state_attacking_update(struct battle_entity_state_attacking *atk, unsigned int ticks)
+{
+	assert(atk);
+
+	return animation_update(&atk->anim, ticks);
+}
+
+void
+battle_entity_state_attacking_draw(const struct battle_entity_state_attacking *atk, const struct battle_entity *et)
+{
+	assert(atk);
+	assert(battle_entity_ok(et));
+
+	animation_draw(&atk->anim, et->x, et->y);
+}
+
+void
+battle_entity_state_attacking(struct battle_entity *et, const struct sprite *which)
+{
+	assert(battle_entity_ok(et));
+	assert(sprite_ok(which));
+
+	struct self *self;
+
+	self = alloc_new0(sizeof (*self));
+	self->state.data = self;
+	self->state.update = update;
+	self->state.draw = draw;
+	self->state.finish = finish;
+
+	battle_entity_state_attacking_init(&self->data, which);
+	battle_entity_switch(et, &self->state);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-rpg/mlk/rpg/battle-entity-state-attacking.h	Sat Oct 15 21:24:17 2022 +0200
@@ -0,0 +1,43 @@
+/*
+ * battle-entity-state-attacking.c -- the entity is attacking
+ *
+ * Copyright (c) 2020-2022 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 MLK_RPG_BATTLE_ENTITY_STATE_ATTACKING_H
+#define MLK_RPG_BATTLE_ENTITY_STATE_ATTACKING_H
+
+#include <mlk/core/animation.h>
+
+struct battle_entity;
+struct sprite;
+
+struct battle_entity_state_attacking {
+	struct animation anim;
+};
+
+void
+battle_entity_state_attacking_init(struct battle_entity_state_attacking *, const struct sprite *);
+
+int
+battle_entity_state_attacking_update(struct battle_entity_state_attacking *, unsigned int);
+
+void
+battle_entity_state_attacking_draw(const struct battle_entity_state_attacking *, const struct battle_entity *);
+
+void
+battle_entity_state_attacking(struct battle_entity *, const struct sprite *);
+
+#endif /* !MLK_RPG_BATTLE_ENTITY_STATE_ATTACKING_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-rpg/mlk/rpg/battle-entity-state-blinking.c	Sat Oct 15 21:24:17 2022 +0200
@@ -0,0 +1,97 @@
+/*
+ * battle-entity-state-blinking.c -- the entity is blinking
+ *
+ * Copyright (c) 2020-2022 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 <stdlib.h>
+
+#include <mlk/core/alloc.h>
+#include <mlk/core/panic.h>
+#include <mlk/core/sprite.h>
+#include <mlk/core/texture.h>
+
+#include "battle-entity-state-blinking.h"
+#include "battle-entity-state.h"
+#include "battle-entity.h"
+#include "character.h"
+
+#define TRANSPARENT     (150)
+#define OPAQUE          (255)
+
+struct self {
+	struct battle_entity_state_blinking data;
+	struct battle_entity_state state;
+};
+
+static int
+update(struct battle_entity_state *st, struct battle_entity *et, unsigned int ticks)
+{
+	(void)et;
+
+	return battle_entity_state_blinking_update(st->data, ticks);
+}
+
+static void
+finish(struct battle_entity_state *st, struct battle_entity *et)
+{
+	(void)et;
+
+	free(st->data);
+}
+
+void
+battle_entity_state_blinking_init(struct battle_entity_state_blinking *blk, struct battle_entity *et)
+{
+	assert(blk);
+	assert(battle_entity_ok(et));
+
+	blk->tex = et->ch->sprites[CHARACTER_SPRITE_NORMAL]->texture;
+	texture_set_alpha_mod(blk->tex, TRANSPARENT);
+}
+
+int
+battle_entity_state_blinking_update(struct battle_entity_state_blinking *blk, unsigned int ticks)
+{
+	assert(blk);
+
+	blk->elapsed += ticks;
+
+	if (blk->elapsed >= 80) {
+		blk->count += 1;
+		blk->elapsed = 0;
+	}
+
+	texture_set_alpha_mod(blk->tex, blk->count % 2 == 0 ? TRANSPARENT : OPAQUE);
+
+	return blk->count >= 3;
+}
+
+void
+battle_entity_state_blinking(struct battle_entity *et)
+{
+	assert(battle_entity_ok(et));
+
+	struct self *self;
+
+	self = alloc_new0(sizeof (*self));
+	self->state.data = self;
+	self->state.update = update;
+	self->state.finish = finish;
+
+	battle_entity_state_blinking_init(&self->data, et);
+	battle_entity_switch(et, &self->state);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-rpg/mlk/rpg/battle-entity-state-blinking.h	Sat Oct 15 21:24:17 2022 +0200
@@ -0,0 +1,40 @@
+/*
+ * battle-entity-state-blinking.h -- the entity is blinking
+ *
+ * Copyright (c) 2020-2022 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 MLK_RPG_BATTLE_ENTITY_STATE_BLINKING_H
+#define MLK_RPG_BATTLE_ENTITY_STATE_BLINKING_H
+
+struct battle_entity;
+struct texture;
+
+struct battle_entity_state_blinking {
+	struct texture *tex;
+	unsigned int elapsed;
+	unsigned int count;
+};
+
+void
+battle_entity_state_blinking_init(struct battle_entity_state_blinking *, struct battle_entity *et);
+
+int
+battle_entity_state_blinking_update(struct battle_entity_state_blinking *, unsigned int);
+
+void
+battle_entity_state_blinking(struct battle_entity *);
+
+#endif /* !MLK_RPG_BATTLE_ENTITY_STATE_BLINKING_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-rpg/mlk/rpg/battle-entity-state-moving.c	Sat Oct 15 21:24:17 2022 +0200
@@ -0,0 +1,129 @@
+/*
+ * battle-entity-state-moving.c -- the entity is moving
+ *
+ * Copyright (c) 2020-2022 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 <math.h>
+#include <stdlib.h>
+
+#include <mlk/core/alloc.h>
+#include <mlk/core/panic.h>
+
+#include "battle-entity-state-moving.h"
+#include "battle-entity-state.h"
+#include "battle-entity.h"
+#include "character.h"
+#include "walksprite.h"
+
+#define SPEED 800
+#define SEC   1000
+#define WALK  40
+
+struct self {
+	struct battle_entity_state_moving data;
+	struct battle_entity_state state;
+};
+
+static inline unsigned int
+orientation(const struct battle_entity_state_moving *mv, const struct battle_entity *et)
+{
+	/* TODO: support diagonal. */
+	/* See: walksprite definitions. */
+	return mv->x < et->x ? 6 : 2;
+}
+
+static int
+update(struct battle_entity_state *st, struct battle_entity *et, unsigned int ticks)
+{
+	return battle_entity_state_moving_update(st->data, et, ticks);
+}
+
+static void
+draw(const struct battle_entity_state *st, const struct battle_entity *et)
+{
+	battle_entity_state_moving_draw(st->data, et);
+}
+
+static void
+finish(struct battle_entity_state *st, struct battle_entity *et)
+{
+	(void)et;
+
+	free(st->data);
+}
+
+void
+battle_entity_state_moving_init(struct battle_entity_state_moving *mv, struct battle_entity *et, int dstx, int dsty)
+{
+	assert(mv);
+	assert(battle_entity_ok(et));
+
+	walksprite_init(&mv->ws, et->ch->sprites[CHARACTER_SPRITE_NORMAL], 40);
+	mv->x = dstx;
+	mv->y = dsty;
+}
+
+int
+battle_entity_state_moving_update(struct battle_entity_state_moving *mv, struct battle_entity *et, unsigned int ticks)
+{
+	assert(mv);
+	assert(battle_entity_ok(et));
+
+	int step_x, step_y, delta_x, delta_y;
+
+	delta_x = mv->x < et->x ? -1 : +1;
+	delta_y = mv->y < et->y ? -1 : +1;
+	step_x = fmin(SPEED * ticks / SEC, abs(et->x - mv->x));
+	step_y = fmin(SPEED * ticks / SEC, abs(et->y - mv->y));
+
+	et->x += delta_x * step_x;
+	et->y += delta_y * step_y;
+
+	if (et->x != mv->x || et->y != mv->y)
+		walksprite_update(&mv->ws, ticks);
+	else
+		walksprite_reset(&mv->ws);
+
+	return et->x == mv->x && et->y == mv->y;
+}
+
+void
+battle_entity_state_moving_draw(const struct battle_entity_state_moving *mv, const struct battle_entity *et)
+{
+	assert(mv);
+	assert(battle_entity_ok(et));
+
+	/* TODO: compute orientation. */
+	walksprite_draw(&mv->ws, orientation(mv, et), et->x, et->y);
+}
+
+void
+battle_entity_state_moving(struct battle_entity *et, int dstx, int dsty)
+{
+	assert(battle_entity_ok(et));
+
+	struct self *self;
+
+	self = alloc_new0(sizeof (*self));
+	self->state.data = self;
+	self->state.update = update;
+	self->state.draw = draw;
+	self->state.finish = finish;
+
+	battle_entity_state_moving_init(&self->data, et, dstx, dsty);
+	battle_entity_switch(et, &self->state);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-rpg/mlk/rpg/battle-entity-state-moving.h	Sat Oct 15 21:24:17 2022 +0200
@@ -0,0 +1,44 @@
+/*
+ * battle-entity-state-moving.h -- the entity is moving
+ *
+ * Copyright (c) 2020-2022 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 MLK_RPG_BATTLE_ENTITY_STATE_MOVING_H
+#define MLK_RPG_BATTLE_ENTITY_STATE_MOVING_H
+
+#include <mlk/rpg/walksprite.h>
+
+struct battle_entity;
+
+struct battle_entity_state_moving {
+	struct walksprite ws;
+	int x;
+	int y;
+};
+
+void
+battle_entity_state_moving_init(struct battle_entity_state_moving *, struct battle_entity *, int, int);
+
+int
+battle_entity_state_moving_update(struct battle_entity_state_moving *, struct battle_entity *, unsigned int);
+
+void
+battle_entity_state_moving_draw(const struct battle_entity_state_moving *, const struct battle_entity *);
+
+void
+battle_entity_state_moving(struct battle_entity *, int, int);
+
+#endif /* !MLK_RPG_BATTLE_ENTITY_STATE_MOVING_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-rpg/mlk/rpg/battle-entity-state-normal.c	Sat Oct 15 21:24:17 2022 +0200
@@ -0,0 +1,36 @@
+/*
+ * battle-entity-state-normal.c -- the entity is normal
+ *
+ * Copyright (c) 2020-2022 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 "battle-entity-state-normal.h"
+#include "battle-entity-state.h"
+#include "battle-entity.h"
+
+/* TODO: animate characters when they are inactive. */
+
+void
+battle_entity_state_normal(struct battle_entity *et)
+{
+	assert(battle_entity_ok(et));
+
+	/* Not needed yet. */
+	static struct battle_entity_state st;
+
+	battle_entity_switch(et, &st);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-rpg/mlk/rpg/battle-entity-state-normal.h	Sat Oct 15 21:24:17 2022 +0200
@@ -0,0 +1,28 @@
+/*
+ * battle-entity-state-normal.h -- the entity is normal
+ *
+ * Copyright (c) 2020-2022 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 MLK_RPG_BATTLE_ENTITY_STATE_NORMAL_H
+#define MLK_RPG_BATTLE_ENTITY_STATE_NORMAL_H
+
+struct battle_entity;
+
+void
+battle_entity_state_normal(struct battle_entity *);
+
+#endif /* !MLK_RPG_BATTLE_ENTITY_STATE_NORMAL_H */
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-rpg/mlk/rpg/battle-entity-state.c	Sat Oct 15 21:24:17 2022 +0200
@@ -0,0 +1,55 @@
+/*
+ * battle-entity-state.c -- abstract battle entity state
+ *
+ * Copyright (c) 2020-2022 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 "battle-entity.h"
+#include "battle-entity-state.h"
+
+int
+battle_entity_state_update(struct battle_entity_state *st, struct battle_entity *et, unsigned int ticks)
+{
+	assert(st);
+	assert(battle_entity_ok(et));
+
+	if (st->update)
+		return st->update(st, et, ticks);
+
+	return 1;
+}
+
+void
+battle_entity_state_draw(const struct battle_entity_state *st, const struct battle_entity *et)
+{
+	assert(st);
+	assert(battle_entity_ok(et));
+
+	if (st->draw)
+		st->draw(st, et);
+	else
+		battle_entity_draw_sprite(et);
+}
+
+void
+battle_entity_state_finish(struct battle_entity_state *st, struct battle_entity *et)
+{
+	assert(battle_entity_ok(et));
+
+	if (st->finish)
+		st->finish(st, et);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-rpg/mlk/rpg/battle-entity-state.h	Sat Oct 15 21:24:17 2022 +0200
@@ -0,0 +1,47 @@
+/*
+ * battle-entity-state.h -- abstract battle entity state
+ *
+ * Copyright (c) 2020-2022 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 MLK_RPG_BATTLE_ENTITY_STATE_H
+#define MLK_RPG_BATTLE_ENTITY_STATE_H
+
+#include <mlk/core/core.h>
+
+struct battle_entity;
+struct sprite;
+
+struct battle_entity_state {
+	void *data;
+	int (*update)(struct battle_entity_state *, struct battle_entity *, unsigned int);
+	void (*draw)(const struct battle_entity_state *, const struct battle_entity *);
+	void (*finish)(struct battle_entity_state *, struct battle_entity *);
+};
+
+CORE_BEGIN_DECLS
+
+int
+battle_entity_state_update(struct battle_entity_state *, struct battle_entity *, unsigned int);
+
+void
+battle_entity_state_draw(const struct battle_entity_state *, const struct battle_entity *);
+
+void
+battle_entity_state_finish(struct battle_entity_state *, struct battle_entity *);
+
+CORE_END_DECLS
+
+#endif /* !MLK_RPG_BATTLE_ENTITY_STATE_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-rpg/mlk/rpg/battle-entity.c	Sat Oct 15 21:24:17 2022 +0200
@@ -0,0 +1,119 @@
+/*
+ * battle-entity.c -- in game battle entity
+ *
+ * Copyright (c) 2020-2022 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 <mlk/core/sprite.h>
+#include <mlk/core/texture.h>
+
+#include <mlk/ui/theme.h>
+
+#include "battle.h"
+#include "battle-entity.h"
+#include "battle-entity-state.h"
+#include "battle-entity-state-normal.h"
+#include "character.h"
+
+static void
+draw_name(const struct battle_entity *et, const struct battle *bt)
+{
+	struct label label = et->name;
+
+	label.theme = BATTLE_THEME(bt);
+
+	if (et == battle_current(bt))
+		label.flags |=  LABEL_FLAGS_SELECTED;
+	else
+		label.flags &= ~LABEL_FLAGS_SELECTED;
+
+	label_draw(&label);
+}
+
+void
+battle_entity_init(struct battle_entity *et)
+{
+	assert(et);
+
+	character_reset(et->ch);
+	texture_set_alpha_mod(et->ch->sprites[CHARACTER_SPRITE_NORMAL]->texture, 255);
+
+	battle_entity_state_normal(et);
+}
+
+int
+battle_entity_ok(const struct battle_entity *et)
+{
+	return et && character_ok(et->ch);
+}
+
+void
+battle_entity_switch(struct battle_entity *et, struct battle_entity_state *st)
+{
+	assert(et);
+	assert(st);
+
+	if (et->state)
+		battle_entity_state_finish(et->state, et);
+
+	et->state = st;
+}
+
+int
+battle_entity_update(struct battle_entity *et, unsigned int ticks)
+{
+	assert(et);
+
+	return battle_entity_state_update(et->state, et, ticks);
+}
+
+void
+battle_entity_draw_sprite(const struct battle_entity *et)
+{
+	struct sprite *sprite = et->ch->sprites[CHARACTER_SPRITE_NORMAL];
+	int row;
+
+	/*
+	 * Ennemies are usually defined with a single image as such the
+	 * sprite may contain only one cell/row. Otherwise if the user
+	 * have provided a structured sprite, use appropriate row.
+	 */
+	if (sprite->nrows >= 6)
+		row = 6;
+	else
+		row = 0;
+
+	sprite_draw(sprite, row, 0, et->x, et->y);
+}
+
+void
+battle_entity_draw(const struct battle_entity *et, const struct battle *bt)
+{
+	assert(et);
+	assert(bt);
+
+	draw_name(et, bt);
+	battle_entity_state_draw(et->state, et);
+}
+
+void
+battle_entity_finish(struct battle_entity *et)
+{
+	assert(et);
+
+	battle_entity_state_finish(et->state, et);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-rpg/mlk/rpg/battle-entity.h	Sat Oct 15 21:24:17 2022 +0200
@@ -0,0 +1,63 @@
+/*
+ * battle-entity.h -- in game battle entity
+ *
+ * Copyright (c) 2020-2022 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 MLK_RPG_BATTLE_ENTITY_H
+#define MLK_RPG_BATTLE_ENTITY_H
+
+#include <mlk/core/core.h>
+
+#include <mlk/ui/label.h>
+
+struct battle;
+struct battle_entity_state;
+struct character;
+
+struct battle_entity {
+	struct character *ch;
+	int x;
+	int y;
+	struct label name;
+	struct battle_entity_state *state;
+};
+
+CORE_BEGIN_DECLS
+
+void
+battle_entity_init(struct battle_entity *);
+
+int
+battle_entity_ok(const struct battle_entity *);
+
+void
+battle_entity_switch(struct battle_entity *, struct battle_entity_state *);
+
+int
+battle_entity_update(struct battle_entity *, unsigned int);
+
+void
+battle_entity_draw(const struct battle_entity *, const struct battle *);
+
+void
+battle_entity_draw_sprite(const struct battle_entity *);
+
+void
+battle_entity_finish(struct battle_entity *);
+
+CORE_END_DECLS
+
+#endif /* !MLK_RPG_BATTLE_ENTITY_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-rpg/mlk/rpg/battle-indicator.c	Sat Oct 15 21:24:17 2022 +0200
@@ -0,0 +1,134 @@
+/*
+ * battle-indicator.c -- drawable for rendering a hp/mp count usage
+ *
+ * Copyright (c) 2020-2022 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 <math.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <mlk/core/color.h>
+#include <mlk/core/font.h>
+#include <mlk/core/panic.h>
+
+#include <mlk/ui/theme.h>
+
+#include "battle-indicator.h"
+
+#define THEME(bti)      ((bti)->theme ? (bti)->theme : theme_default())
+#define STEP            (2)
+#define DELAY           (5)
+
+static inline unsigned int
+inc(int cmp, int tgt)
+{
+	return tgt > cmp ? fmin(cmp + STEP, tgt) : fmax(cmp - STEP, tgt);
+}
+
+static inline int
+colored(const struct battle_indicator *bti)
+{
+	/* Only check r, g, b and ignore alpha. */
+	return COLOR_R(bti->cur) == COLOR_R(bti->color) &&
+	       COLOR_G(bti->cur) == COLOR_G(bti->color) &&
+	       COLOR_B(bti->cur) == COLOR_B(bti->color);
+}
+
+void
+battle_indicator_start(struct battle_indicator *bti)
+{
+	assert(bti);
+
+	char buf[128];
+	const struct theme *theme = THEME(bti);
+
+	snprintf(buf, sizeof (buf), "%u", bti->amount);
+
+	bti->cur = 0xffffffff;
+	bti->elapsed = 0;
+	bti->alpha = 250;
+
+	if (font_render(theme->fonts[THEME_FONT_INTERFACE], &bti->tex[0], buf, bti->cur) < 0||
+	    font_render(theme->fonts[THEME_FONT_INTERFACE], &bti->tex[1], buf, 0x000000ff) < 0)
+		panic();
+}
+
+int
+battle_indicator_completed(const struct battle_indicator *bti)
+{
+	assert(battle_indicator_ok(bti));
+
+	return colored(bti) && bti->alpha == 0;
+}
+
+int
+battle_indicator_ok(const struct battle_indicator *bti)
+{
+	return bti && texture_ok(&bti->tex[0]) && texture_ok(&bti->tex[1]);
+}
+
+int
+battle_indicator_update(struct battle_indicator *bti, unsigned int ticks)
+{
+	assert(battle_indicator_ok(bti));
+
+	bti->elapsed += ticks;
+
+	if (bti->elapsed > DELAY) {
+		bti->elapsed = 0;
+
+		if (!colored(bti)) {
+			/* Update colors first. */
+			bti->cur = COLOR_HEX(
+			    inc(COLOR_R(bti->cur), COLOR_R(bti->color)),
+			    inc(COLOR_G(bti->cur), COLOR_G(bti->color)),
+			    inc(COLOR_B(bti->cur), COLOR_B(bti->color)),
+			    255
+			);
+
+			texture_set_color_mod(&bti->tex[0], bti->cur);
+		} else {
+			/* Update alpha next. */
+			bti->alpha -= 10;
+
+			texture_set_alpha_mod(&bti->tex[0], bti->alpha);
+			texture_set_alpha_mod(&bti->tex[1], bti->alpha);
+		}
+	}
+
+	return battle_indicator_completed(bti);
+}
+
+void
+battle_indicator_draw(const struct battle_indicator *bti, int x, int y)
+{
+	assert(battle_indicator_ok(bti));
+
+	texture_draw(&bti->tex[1], x + 1, y + 1);
+	texture_draw(&bti->tex[0], x, y);
+}
+
+void
+battle_indicator_finish(struct battle_indicator *bti)
+{
+	assert(bti);
+
+	texture_finish(&bti->tex[0]);
+	texture_finish(&bti->tex[1]);
+
+	memset(bti, 0, sizeof (*bti));
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-rpg/mlk/rpg/battle-indicator.h	Sat Oct 15 21:24:17 2022 +0200
@@ -0,0 +1,62 @@
+/*
+ * battle-indicator.h -- drawable for rendering a hp/mp count usage
+ *
+ * Copyright (c) 2020-2022 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 MLK_RPG_BATTLE_INDICATOR_H
+#define MLK_RPG_BATTLE_INDICATOR_H
+
+#include <mlk/core/core.h>
+#include <mlk/core/texture.h>
+
+#define BATTLE_INDICATOR_HP_COLOR (0xa5303000)
+#define BATTLE_INDICATOR_MP_COLOR (0xa23e8c00)
+
+struct theme;
+
+struct battle_indicator {
+	unsigned int color;
+	unsigned int amount;
+	const struct theme *theme;
+	unsigned int cur;
+	unsigned int elapsed;
+	unsigned int alpha;
+	struct texture tex[2];
+};
+
+CORE_BEGIN_DECLS
+
+void
+battle_indicator_start(struct battle_indicator *);
+
+int
+battle_indicator_completed(const struct battle_indicator *);
+
+int
+battle_indicator_ok(const struct battle_indicator *);
+
+int
+battle_indicator_update(struct battle_indicator *, unsigned int);
+
+void
+battle_indicator_draw(const struct battle_indicator *, int, int);
+
+void
+battle_indicator_finish(struct battle_indicator *);
+
+CORE_END_DECLS
+
+#endif /* !MLK_RPG_BATTLE_INDICATOR_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-rpg/mlk/rpg/battle-message.c	Sat Oct 15 21:24:17 2022 +0200
@@ -0,0 +1,69 @@
+/*
+ * battle-message.c -- automatic top center message in battle
+ *
+ * Copyright (c) 2020-2022 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 <mlk/core/window.h>
+
+#include <mlk/ui/align.h>
+#include <mlk/ui/frame.h>
+#include <mlk/ui/label.h>
+
+#include "battle-message.h"
+
+#define DELAY 1500
+
+int
+battle_message_update(struct battle_message *msg, unsigned int ticks)
+{
+	assert(msg);
+
+	msg->elapsed += ticks;
+
+	return msg->elapsed >= DELAY;
+}
+
+void
+battle_message_draw(const struct battle_message *msg)
+{
+	assert(msg);
+
+	struct frame f = {0};
+	struct label l = {0};
+	unsigned int lw = 0, lh = 0;
+
+	/* Prepare message frame. */
+	f.w = window.w / 3;
+	f.h = window.h / 15;
+	f.theme = msg->theme;
+
+	/* Center on top. */
+	align(ALIGN_TOP, &f.x, &f.y, f.w, f.h, 0, 20, window.w, window.h);
+
+	/* Prepare message label box. */
+	l.text = msg->text;
+	l.flags = LABEL_FLAGS_SHADOW;
+	l.theme = msg->theme;
+	label_query(&l, &lw, &lh);
+
+	/* Align the text in the box. */
+	align(ALIGN_CENTER, &l.x, &l.y, lw, lh, f.x, f.y, f.w, f.h);
+
+	frame_draw(&f);
+	label_draw(&l);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-rpg/mlk/rpg/battle-message.h	Sat Oct 15 21:24:17 2022 +0200
@@ -0,0 +1,42 @@
+/*
+ * battle-message.h -- automatic top center message in battle
+ *
+ * Copyright (c) 2020-2022 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 MLK_RPG_BATTLE_MESSAGE_H
+#define MLK_RPG_BATTLE_MESSAGE_H
+
+#include <mlk/core/core.h>
+
+struct theme;
+
+struct battle_message {
+	const char *text;
+	struct theme *theme;
+	unsigned int elapsed;
+};
+
+CORE_BEGIN_DECLS
+
+int
+battle_message_update(struct battle_message *, unsigned int);
+
+void
+battle_message_draw(const struct battle_message *);
+
+CORE_END_DECLS
+
+#endif /* !MLK_RPG_BATTLE_MESSAGE_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-rpg/mlk/rpg/battle-state-ai.c	Sat Oct 15 21:24:17 2022 +0200
@@ -0,0 +1,92 @@
+/*
+ * battle-state-ai.c -- battle state (enemy is playing)
+ *
+ * Copyright (c) 2020-2022 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 <stdlib.h>
+
+#include <mlk/core/alloc.h>
+
+#include "battle-state-ai.h"
+#include "battle-state.h"
+#include "battle.h"
+#include "character.h"
+
+static int
+update(struct battle_state *st, struct battle *bt, unsigned int ticks)
+{
+	(void)st;
+	(void)ticks;
+
+	battle_state_ai_update(bt);
+
+	return 0;
+}
+
+static void
+draw(const struct battle_state *st, const struct battle *bt)
+{
+	(void)st;
+
+	battle_state_ai_draw(bt);
+}
+
+static void
+finish(struct battle_state *st, struct battle *bt)
+{
+	(void)bt;
+
+	free(st);
+}
+
+void
+battle_state_ai_update(struct battle *bt)
+{
+	assert(battle_ok(bt));
+
+	struct character *ch = battle_current(bt)->ch;
+
+	/*
+	 * Immediately invoke the enemy exec strategy and put the battle state
+	 * to check.
+	 */
+	character_exec(ch, bt);
+}
+
+void
+battle_state_ai_draw(const struct battle *bt)
+{
+	assert(battle_ok(bt));
+
+	battle_draw_component(bt, BATTLE_COMPONENT_ALL);
+}
+
+void
+battle_state_ai(struct battle *bt)
+{
+	assert(bt);
+
+	struct battle_state *self;
+
+	self = alloc_new0(sizeof (*self));
+	self->data = bt;
+	self->update = update;
+	self->draw = draw;
+	self->finish = finish;
+
+	battle_switch(bt, self);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-rpg/mlk/rpg/battle-state-ai.h	Sat Oct 15 21:24:17 2022 +0200
@@ -0,0 +1,33 @@
+/*
+ * battle-state-ai.h -- battle state (enemy is playing)
+ *
+ * Copyright (c) 2020-2022 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 MLK_RPG_BATTLE_STATE_AI_H
+#define MLK_RPG_BATTLE_STATE_AI_H
+
+struct battle;
+
+void
+battle_state_ai_update(struct battle *);
+
+void
+battle_state_ai_draw(const struct battle *);
+
+void
+battle_state_ai(struct battle *);
+
+#endif /* !MLK_RPG_BATTLE_STATE_AI_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-rpg/mlk/rpg/battle-state-attacking.c	Sat Oct 15 21:24:17 2022 +0200
@@ -0,0 +1,176 @@
+/*
+ * battle-state-attacking.c -- battle state (entity is moving for attacking)
+ *
+ * Copyright (c) 2020-2022 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 <stdlib.h>
+
+#include <mlk/core/alloc.h>
+#include <mlk/core/panic.h>
+#include <mlk/core/sprite.h>
+
+#include "battle-entity-state-attacking.h"
+#include "battle-entity-state-blinking.h"
+#include "battle-entity-state-moving.h"
+#include "battle-entity-state-normal.h"
+#include "battle-entity-state.h"
+#include "battle-state-attacking.h"
+#include "battle-state-check.h"
+#include "battle-state.h"
+#include "battle.h"
+#include "character.h"
+
+struct self {
+	struct battle_state_attacking data;
+	struct battle_state state;
+};
+
+static void
+damage(const struct battle_entity *source, struct battle_entity *target, struct battle *bt)
+{
+	(void)source;
+
+	/* TODO: math calculation here.*/
+	target->ch->hp -= 50;
+
+	if (target->ch->hp < 0)
+		target->ch->hp = 0;
+
+	battle_indicator_hp(bt, target->ch, 50);
+}
+
+static int
+update(struct battle_state *st, struct battle *bt, unsigned int ticks)
+{
+	battle_state_attacking_update(st->data, bt, ticks);
+
+	return 0;
+}
+
+static void
+draw(const struct battle_state *st, const struct battle *bt)
+{
+	battle_state_attacking_draw(st->data, bt);
+}
+
+static void
+finish(struct battle_state *st, struct battle *bt)
+{
+	(void)bt;
+
+	free(st->data);
+}
+
+void
+battle_state_attacking_init(struct battle_state_attacking *atk,
+                            struct battle_entity *source,
+                            struct battle_entity *target)
+{
+	assert(atk);
+	assert(battle_entity_ok(source));
+	assert(battle_entity_ok(target));
+
+	int x, y;
+
+	/* Starts this state with advancing. */
+	atk->source = source;
+	atk->target = target;
+	atk->origin_x = source->x;
+	atk->origin_y = source->y;
+
+	/* We go to the enemy. */
+	x = target->x + target->ch->sprites[CHARACTER_SPRITE_NORMAL]->cellw;
+	y = target->y;
+
+	/* If it is an enemy we don't move it but blink instead. */
+	if (source->ch->exec) {
+		atk->status = BATTLE_STATE_ATTACKING_BLINKING;
+		battle_entity_state_blinking(source);
+	} else
+		battle_entity_state_moving(source, x, y);
+}
+
+void
+battle_state_attacking_update(struct battle_state_attacking *atk, struct battle *bt, unsigned int ticks)
+{
+	assert(atk);
+	assert(bt);
+
+	battle_update_component(bt, ticks, BATTLE_COMPONENT_ALL);
+
+	if (!battle_entity_update(atk->source, 0))
+		return;
+
+	switch (atk->status) {
+	case BATTLE_STATE_ATTACKING_ADVANCING:
+		/*
+		 * Current entity state is battle-entity-state-moving but it is
+		 * already updated from the game itself so pass 0 just to check
+		 * if it has finished moving.
+		 */
+		atk->status = BATTLE_STATE_ATTACKING_DAMAGING;
+
+		/* TODO: determine sprite to use about equipment. */
+		battle_entity_state_attacking(atk->source, atk->source->ch->sprites[CHARACTER_SPRITE_SWORD]);
+		break;
+	case BATTLE_STATE_ATTACKING_DAMAGING:
+		/* Move back to original position. */
+		atk->status = BATTLE_STATE_ATTACKING_RETURNING;
+		damage(atk->source, atk->target, bt);
+		battle_entity_state_moving(atk->source, atk->origin_x, atk->origin_y);
+		break;
+	case BATTLE_STATE_ATTACKING_RETURNING:
+	case BATTLE_STATE_ATTACKING_BLINKING:
+		/* Just wait. */
+		battle_entity_state_normal(atk->source);
+		damage(atk->source, atk->target, bt);
+		battle_state_check(bt);
+		break;
+	default:
+		break;
+	}
+}
+
+void
+battle_state_attacking_draw(const struct battle_state_attacking *atk, const struct battle *bt)
+{
+	assert(atk);
+	assert(battle_ok(bt));
+
+	(void)atk;
+
+	battle_draw_component(bt, BATTLE_COMPONENT_ALL);
+}
+
+void
+battle_state_attacking(struct battle_entity *source, struct battle_entity *target, struct battle *bt)
+{
+	assert(battle_entity_ok(source));
+	assert(battle_entity_ok(target));
+	assert(bt);
+
+	struct self *self;
+
+	self = alloc_new0(sizeof (*self));
+	self->state.data = self;
+	self->state.update = update;
+	self->state.draw = draw;
+	self->state.finish = finish;
+
+	battle_state_attacking_init(&self->data, source, target);
+	battle_switch(bt, &self->state);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-rpg/mlk/rpg/battle-state-attacking.h	Sat Oct 15 21:24:17 2022 +0200
@@ -0,0 +1,57 @@
+/*
+ * battle-state-attacking.h -- battle state (entity is moving for attacking)
+ *
+ * Copyright (c) 2020-2022 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 MLK_RPG_BATTLE_STATE_ATTACKING_H
+#define MLK_RPG_BATTLE_STATE_ATTACKING_H
+
+struct battle;
+struct battle_entity;
+
+enum battle_state_attacking_status {
+	/* For team. */
+	BATTLE_STATE_ATTACKING_ADVANCING,
+	BATTLE_STATE_ATTACKING_DAMAGING,
+	BATTLE_STATE_ATTACKING_RETURNING,
+
+	/* For enemies. */
+	BATTLE_STATE_ATTACKING_BLINKING,
+};
+
+struct battle_state_attacking {
+	enum battle_state_attacking_status status;
+	struct battle_entity *source;
+	struct battle_entity *target;
+	int origin_x;
+	int origin_y;
+};
+
+void
+battle_state_attacking_init(struct battle_state_attacking *,
+                            struct battle_entity *,
+                            struct battle_entity *);
+
+void
+battle_state_attacking_update(struct battle_state_attacking *, struct battle *, unsigned int);
+
+void
+battle_state_attacking_draw(const struct battle_state_attacking *, const struct battle *);
+
+void
+battle_state_attacking(struct battle_entity *, struct battle_entity *, struct battle *);
+
+#endif /* !#define MLK_RPG_BATTLE_STATE_ATTACKING_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-rpg/mlk/rpg/battle-state-check.c	Sat Oct 15 21:24:17 2022 +0200
@@ -0,0 +1,207 @@
+/*
+ * battle-state-check.c -- battle state (check status)
+ *
+ * Copyright (c) 2020-2022 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 <stdlib.h>
+#include <string.h>
+
+#include <mlk/core/alloc.h>
+#include <mlk/core/panic.h>
+#include <mlk/core/sprite.h>
+#include <mlk/core/texture.h>
+#include <mlk/core/trace.h>
+
+#include "battle-state-check.h"
+#include "battle-state-lost.h"
+#include "battle-state-victory.h"
+#include "battle-state.h"
+#include "battle.h"
+#include "character.h"
+
+struct fadeout {
+	struct character *ch;
+	int x;
+	int y;
+	struct drawable dw;
+	unsigned int alpha;
+	unsigned int elapsed;
+};
+
+static int
+fadeout_update(struct drawable *dw, unsigned int ticks)
+{
+	struct fadeout *fade = dw->data;
+
+	fade->elapsed += ticks;
+
+	if (fade->elapsed >= 8) {
+		fade->elapsed = 0;
+
+		if (fade->alpha == 0)
+			return 1;
+
+		fade->alpha -= 10;
+	}
+
+	return 0;
+}
+
+static void
+fadeout_draw(struct drawable *dw)
+{
+	const struct fadeout *fade = dw->data;
+	struct sprite *sprite = fade->ch->sprites[CHARACTER_SPRITE_NORMAL];
+
+	texture_set_alpha_mod(sprite->texture, fade->alpha);
+	sprite_draw(sprite, 0, 0, fade->x, fade->y);
+	texture_set_alpha_mod(sprite->texture, 255);
+}
+
+static void
+fadeout_finish(struct drawable *dw)
+{
+	free(dw->data);
+}
+
+static void
+fadeout(struct battle *bt, struct battle_entity *et)
+{
+	struct fadeout *fade;
+
+	if (!bt->effects) {
+		tracef("can't create a fadeout effect without a drawable_stack");
+		return;
+	}
+
+	fade = alloc_new0(sizeof (*fade));
+	fade->ch = et->ch;
+	fade->x = et->x;
+	fade->y = et->y;
+	fade->alpha = 250;
+	fade->dw.data = fade;
+	fade->dw.draw = fadeout_draw;
+	fade->dw.update = fadeout_update;
+	fade->dw.finish = fadeout_finish;
+
+	if (drawable_stack_add(bt->effects, &fade->dw) < 0)
+		free(fade);
+}
+
+static int
+is_dead(const struct battle *bt)
+{
+	const struct battle_entity *et;
+
+	BATTLE_TEAM_FOREACH(bt, et) {
+		if (!character_ok(et->ch))
+			continue;
+		if (et->ch->hp > 0)
+			return 0;
+	}
+
+	return 1;
+}
+
+static int
+is_won(const struct battle *bt)
+{
+	const struct battle_entity *et;
+
+	BATTLE_ENEMY_FOREACH(bt, et)
+		if (character_ok(et->ch))
+			return 0;
+
+	return 1;
+}
+
+static void
+clean(struct battle *bt)
+{
+	for (size_t i = 0; i < bt->enemiesz; ++i) {
+		if (bt->enemies[i] && character_ok(bt->enemies[i]->ch) && bt->enemies[i]->ch->hp == 0) {
+			fadeout(bt, bt->enemies[i]);
+			bt->enemies[i] = NULL;
+		}
+	}
+}
+
+static int
+update(struct battle_state *st, struct battle *bt, unsigned int ticks)
+{
+	(void)st;
+	(void)ticks;
+
+	battle_state_check_update(bt);
+
+	return 0;
+}
+
+static void
+draw(const struct battle_state *st, const struct battle *bt)
+{
+	(void)st;
+
+	battle_state_check_draw(bt);
+}
+
+static void
+finish(struct battle_state *st, struct battle *bt)
+{
+	(void)bt;
+
+	free(st);
+}
+
+void
+battle_state_check_update(struct battle *bt)
+{
+	assert(battle_ok(bt));
+
+	clean(bt);
+
+	if (is_dead(bt))
+		battle_state_lost(bt);
+	else if (is_won(bt))
+		battle_state_victory(bt);
+	else
+		battle_next(bt);
+}
+
+void
+battle_state_check_draw(const struct battle *bt)
+{
+	assert(battle_ok(bt));
+
+	battle_draw_component(bt, BATTLE_COMPONENT_ALL);
+}
+
+void
+battle_state_check(struct battle *bt)
+{
+	assert(battle_ok(bt));
+
+	struct battle_state *self;
+
+	self = alloc_new0(sizeof (*self));
+	self->data = bt;
+	self->update = update;
+	self->draw = draw;
+	self->finish = finish;
+
+	battle_switch(bt, self);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-rpg/mlk/rpg/battle-state-check.h	Sat Oct 15 21:24:17 2022 +0200
@@ -0,0 +1,33 @@
+/*
+ * battle-state-check.h -- battle state (check status)
+ *
+ * Copyright (c) 2020-2022 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 MLK_RPG_BATTLE_STATE_CHECK_H
+#define MLK_RPG_BATTLE_STATE_CHECK_H
+
+struct battle;
+
+void
+battle_state_check_update(struct battle *);
+
+void
+battle_state_check_draw(const struct battle *);
+
+void
+battle_state_check(struct battle *);
+
+#endif /* !MLK_RPG_BATTLE_STATE_CHECK_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-rpg/mlk/rpg/battle-state-closing.c	Sat Oct 15 21:24:17 2022 +0200
@@ -0,0 +1,140 @@
+/*
+ * battle-state-closing.c -- battle state (closing)
+ *
+ * Copyright (c) 2020-2022 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 <stdlib.h>
+#include <string.h>
+
+#include <mlk/core/alloc.h>
+#include <mlk/core/music.h>
+#include <mlk/core/painter.h>
+#include <mlk/core/panic.h>
+#include <mlk/core/texture.h>
+#include <mlk/core/window.h>
+
+#include "battle-state-closing.h"
+#include "battle.h"
+
+struct self {
+	struct battle_state_closing data;
+	struct battle_state state;
+};
+
+static int
+update(struct battle_state *st, struct battle *bt, unsigned int ticks)
+{
+	(void)bt;
+
+	return battle_state_closing_update(st->data, ticks);
+}
+
+static void
+draw(const struct battle_state *st, const struct battle *bt)
+{
+	(void)bt;
+
+	battle_state_closing_draw(st->data, bt);
+}
+
+static void
+finish(struct battle_state *st, struct battle *bt)
+{
+	(void)bt;
+
+	battle_state_closing_finish(st->data);
+	free(st->data);
+}
+
+void
+battle_state_closing_init(struct battle_state_closing *cls)
+{
+	assert(cls);
+
+	if (texture_new(&cls->texture, window.w, window.h) < 0)
+		panic();
+
+	PAINTER_BEGIN(&cls->texture);
+	texture_set_blend_mode(&cls->texture, TEXTURE_BLEND_BLEND);
+	painter_set_color(0x000000ff);
+	painter_clear();
+	painter_draw_rectangle(0, 0, window.w, window.h);
+	PAINTER_END();
+
+	texture_set_alpha_mod(&cls->texture, 0);
+}
+
+int
+battle_state_closing_update(struct battle_state_closing *cls, unsigned int ticks)
+{
+	assert(cls);
+
+	cls->elapsed += ticks;
+
+	/* TODO: ??? */
+	if (cls->elapsed > 8) {
+		cls->elapsed = 0;
+
+		if (cls->alpha == 255) {
+			/* TODO: since OpenAL, no notion of global music. */
+#if 0
+			music_stop(0);
+#endif
+			return 1;
+		}
+
+		cls->alpha += 5;
+		texture_set_alpha_mod(&cls->texture, cls->alpha);
+	}
+
+	return 0;
+}
+
+void
+battle_state_closing_draw(const struct battle_state_closing *cls, const struct battle *bt)
+{
+	assert(cls);
+
+	battle_draw_component(bt, BATTLE_COMPONENT_ALL);
+	texture_draw(&cls->texture, 0, 0);
+}
+
+void
+battle_state_closing_finish(struct battle_state_closing *cls)
+{
+	assert(cls);
+
+	texture_finish(&cls->texture);
+	memset(cls, 0, sizeof (*cls));
+}
+
+void
+battle_state_closing(struct battle *bt)
+{
+	assert(bt);
+
+	struct self *self;
+
+	self = alloc_new0(sizeof (*self));
+	self->state.data = self;
+	self->state.update = update;
+	self->state.draw = draw;
+	self->state.finish = finish;
+
+	battle_state_closing_init(&self->data);
+	battle_switch(bt, &self->state);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-rpg/mlk/rpg/battle-state-closing.h	Sat Oct 15 21:24:17 2022 +0200
@@ -0,0 +1,47 @@
+/*
+ * battle-state-closing.h -- battle state (closing)
+ *
+ * Copyright (c) 2020-2022 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 MLK_RPG_BATTLE_STATE_CLOSING_H
+#define MLK_RPG_BATTLE_STATE_CLOSING_H
+
+#include <mlk/core/texture.h>
+
+struct battle;
+
+struct battle_state_closing {
+	struct texture texture;
+	unsigned int alpha;
+	unsigned int elapsed;
+};
+
+void
+battle_state_closing_init(struct battle_state_closing *);
+
+int
+battle_state_closing_update(struct battle_state_closing *, unsigned int);
+
+void
+battle_state_closing_draw(const struct battle_state_closing *, const struct battle *);
+
+void
+battle_state_closing_finish(struct battle_state_closing *);
+
+void
+battle_state_closing(struct battle *);
+
+#endif /* !MLK_RPG_BATTLE_STATE_CLOSING_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-rpg/mlk/rpg/battle-state-item.c	Sat Oct 15 21:24:17 2022 +0200
@@ -0,0 +1,147 @@
+/*
+ * battle-state-item.c -- battle state (using item)
+ *
+ * Copyright (c) 2020-2022 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 <stdlib.h>
+
+#include <mlk/core/alloc.h>
+#include <mlk/core/panic.h>
+
+#include "battle-entity-state-moving.h"
+#include "battle-entity-state-normal.h"
+#include "battle-entity-state.h"
+#include "battle-message.h"
+#include "battle-state-item.h"
+#include "battle-state.h"
+#include "battle.h"
+#include "inventory.h"
+#include "item.h"
+
+struct self {
+	struct battle_state_item data;
+	struct battle_state state;
+};
+
+static int
+update(struct battle_state *st, struct battle *bt, unsigned int ticks)
+{
+	return battle_state_item_update(st->data, bt, ticks);
+}
+
+static void
+draw(const struct battle_state *st, const struct battle *bt)
+{
+	(void)bt;
+
+	battle_state_item_draw(st->data);
+}
+
+static void
+finish(struct battle_state *st, struct battle *bt)
+{
+	(void)bt;
+
+	free(st->data);
+}
+
+void
+battle_state_item_init(struct battle_state_item *it,
+                       struct battle *bt,
+                       struct battle_entity *source,
+                       struct battle_entity *target,
+                       struct inventory_slot *slot)
+{
+	assert(it);
+	assert(bt);
+	assert(battle_entity_ok(source));
+	assert(battle_entity_ok(target));
+	assert(slot);
+
+	it->source = source;
+	it->target = target;
+	it->slot = slot;
+	it->origin_x = it->source->x;
+
+	it->msg.text = slot->item->name;
+	it->msg.theme = bt->theme;
+
+	battle_entity_state_moving(it->source, it->origin_x - 100, it->source->y);
+}
+
+int
+battle_state_item_update(struct battle_state_item *it, struct battle *bt, unsigned int ticks)
+{
+	assert(it);
+	assert(bt);
+
+	switch (it->status) {
+	case BATTLE_STATE_ITEM_ADVANCING:
+		/* Entity is updating from battle, so just inspect its status. */
+		if (battle_entity_update(it->source, 0)) {
+			it->status = BATTLE_STATE_ITEM_MESSAGE;
+			battle_entity_state_normal(it->source);
+		}
+		break;
+	case BATTLE_STATE_ITEM_MESSAGE:
+		if (battle_message_update(&it->msg, ticks)) {
+			it->status = BATTLE_STATE_ITEM_RETURNING;
+			battle_entity_state_moving(it->source, it->origin_x, it->source->y);
+		}
+		break;
+	default:
+		if (battle_entity_update(it->source, 0)) {
+			battle_entity_state_normal(it->source);
+			battle_use(bt, it->slot->item, it->source->ch, it->target->ch);
+		}
+		break;
+	}
+
+	return 0;
+}
+
+void
+battle_state_item_draw(struct battle_state_item *it)
+{
+	assert(it);
+
+	if (it->status == BATTLE_STATE_ITEM_MESSAGE)
+		battle_message_draw(&it->msg);
+}
+
+void
+battle_state_item(struct battle *bt,
+                  struct battle_entity *source,
+                  struct battle_entity *target,
+                  struct inventory_slot *slot)
+{
+	assert(battle_entity_ok(source));
+	assert(battle_entity_ok(target));
+	assert(slot);
+	assert(bt);
+
+	struct self *self;
+
+	self = alloc_new0(sizeof (*self));
+	self->state.data = self;
+	self->state.update = update;
+	self->state.draw = draw;
+	self->state.finish = finish;
+
+	battle_state_item_init(&self->data, bt, source, target, slot);
+	battle_switch(bt, &self->state);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-rpg/mlk/rpg/battle-state-item.h	Sat Oct 15 21:24:17 2022 +0200
@@ -0,0 +1,63 @@
+/*
+ * battle-state-item.h -- battle state (using item)
+ *
+ * Copyright (c) 2020-2022 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 MLK_RPG_BATTLE_STATE_ITEM_H
+#define MLK_RPG_BATTLE_STATE_ITEM_H
+
+#include <mlk/rpg/battle-message.h>
+
+struct battle;
+struct battle_entity;
+struct inventory_slot;
+
+enum battle_state_item_status {
+	BATTLE_STATE_ITEM_ADVANCING,
+	BATTLE_STATE_ITEM_MESSAGE,
+	BATTLE_STATE_ITEM_RETURNING
+};
+
+struct battle_state_item {
+	enum battle_state_item_status status;
+	struct battle_message msg;
+	struct battle_entity *source;
+	struct battle_entity *target;
+	struct inventory_slot *slot;
+	int origin_x;
+};
+
+void
+battle_state_item_init(struct battle_state_item *,
+                       struct battle *,
+                       struct battle_entity *,
+                       struct battle_entity *,
+                       struct inventory_slot *);
+
+int
+battle_state_item_update(struct battle_state_item *, struct battle *, unsigned int);
+
+void
+battle_state_item_draw(struct battle_state_item *);
+
+void
+battle_state_item(struct battle *,
+                  struct battle_entity *,
+                  struct battle_entity *,
+                  struct inventory_slot *);
+
+
+#endif /* !MLK_RPG_BATTLE_STATE_ITEM_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-rpg/mlk/rpg/battle-state-lost.c	Sat Oct 15 21:24:17 2022 +0200
@@ -0,0 +1,134 @@
+/*
+ * battle-state-lost.c -- battle state (lost)
+ *
+ * Copyright (c) 2020-2022 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 <mlk/core/alloc.h>
+#include <mlk/core/music.h>
+#include <mlk/core/panic.h>
+#include <mlk/core/window.h>
+
+#include "battle-state-closing.h"
+#include "battle-state-lost.h"
+#include "battle-state.h"
+#include "battle.h"
+
+struct self {
+	struct battle_state_lost data;
+	struct battle_state state;
+};
+
+static void
+handle(struct battle_state *st, struct battle *bt, const union event *ev)
+{
+	(void)bt;
+
+	battle_state_lost_handle(st->data, ev);
+}
+
+static int
+update(struct battle_state *st, struct battle *bt, unsigned int ticks)
+{
+	return battle_state_lost_update(st->data, bt, ticks);
+}
+
+static void
+draw(const struct battle_state *st, const struct battle *bt)
+{
+	(void)bt;
+
+	battle_state_lost_draw(st->data, bt);
+}
+
+void
+battle_state_lost_init(struct battle_state_lost *lost, struct battle *bt)
+{
+	assert(lost);
+	assert(bt);
+
+	lost->text = "You have been defeated...";
+
+	lost->msg.lines = &lost->text;
+	lost->msg.linesz = 1;
+	lost->msg.theme = bt->theme;
+	lost->msg.flags = MESSAGE_FLAGS_AUTOMATIC |
+	                  MESSAGE_FLAGS_FADEIN |
+	                  MESSAGE_FLAGS_FADEOUT;
+	lost->msg.timeout = MESSAGE_TIMEOUT_DEFAULT;
+	lost->msg.delay = MESSAGE_DELAY_DEFAULT;
+
+	message_start(&lost->msg);
+	message_query(&lost->msg, NULL, &lost->msg.h);
+
+	lost->msg.w = window.w * 0.6;
+	lost->msg.y = window.h * 0.1;
+	lost->msg.x = (window.w - lost->msg.w) / 2;
+
+	bt->status = BATTLE_STATUS_LOST;
+
+	if (bt->music[2])
+		music_play(bt->music[2], MUSIC_NONE);
+}
+
+void
+battle_state_lost_handle(struct battle_state_lost *lost, const union event *ev)
+{
+	assert(lost);
+	assert(ev);
+
+	message_handle(&lost->msg, ev);
+}
+
+int
+battle_state_lost_update(struct battle_state_lost *lost, struct battle *bt, unsigned int ticks)
+{
+	assert(lost);
+	assert(bt);
+
+	if (message_update(&lost->msg, ticks))
+		battle_state_closing(bt);
+
+	return 0;
+}
+
+void
+battle_state_lost_draw(struct battle_state_lost *lost, const struct battle *bt)
+{
+	assert(lost);
+	assert(battle_ok(bt));
+
+	battle_draw_component(bt, BATTLE_COMPONENT_ALL);
+	message_draw(&lost->msg);
+}
+
+void
+battle_state_lost(struct battle *bt)
+{
+	assert(bt);
+
+	struct self *self;
+
+	self = alloc_new0(sizeof (*self));
+	self->state.data = self;
+	self->state.handle = handle;
+	self->state.update = update;
+	self->state.draw = draw;
+
+	battle_state_lost_init(&self->data, bt);
+	battle_switch(bt, &self->state);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-rpg/mlk/rpg/battle-state-lost.h	Sat Oct 15 21:24:17 2022 +0200
@@ -0,0 +1,48 @@
+/*
+ * battle-state-lost.h -- battle state (lost)
+ *
+ * Copyright (c) 2020-2022 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 MLK_RPG_BATTLE_STATE_LOST_H
+#define MLK_RPG_BATTLE_STATE_LOST_H
+
+#include <mlk/rpg/message.h>
+
+union event;
+
+struct battle;
+
+struct battle_state_lost {
+	const char *text;
+	struct message msg;
+};
+
+void
+battle_state_lost_init(struct battle_state_lost *, struct battle *);
+
+void
+battle_state_lost_handle(struct battle_state_lost *, const union event *);
+
+int
+battle_state_lost_update(struct battle_state_lost *, struct battle *, unsigned int);
+
+void
+battle_state_lost_draw(struct battle_state_lost *, const struct battle *);
+
+void
+battle_state_lost(struct battle *);
+
+#endif /* !MLK_RPG_BATTLE_STATE_LOST_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-rpg/mlk/rpg/battle-state-menu.c	Sat Oct 15 21:24:17 2022 +0200
@@ -0,0 +1,104 @@
+/*
+ * battle-state-menu.h -- battle state (menu)
+ *
+ * Copyright (c) 2020-2022 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 <stdlib.h>
+
+#include <mlk/core/alloc.h>
+
+#include "battle-bar.h"
+#include "battle-state-menu.h"
+#include "battle-state.h"
+#include "battle.h"
+
+static void
+handle(struct battle_state *st, struct battle *bt, const union event *ev)
+{
+	(void)st;
+
+	battle_state_menu_handle(bt, ev);
+}
+
+static int
+update(struct battle_state *st, struct battle *bt, unsigned int ticks)
+{
+	(void)st;
+
+	battle_state_menu_update(bt, ticks);
+
+	return 0;
+}
+
+static void
+draw(const struct battle_state *st, const struct battle *bt)
+{
+	(void)st;
+
+	battle_state_menu_draw(bt);
+}
+
+static void
+finish(struct battle_state *st, struct battle *bt)
+{
+	(void)bt;
+
+	free(st);
+}
+
+void
+battle_state_menu_handle(struct battle *bt, const union event *ev)
+{
+	assert(bt);
+	assert(ev);
+
+	battle_bar_handle(bt->bar, bt, ev);
+}
+
+void
+battle_state_menu_update(struct battle *bt, unsigned int ticks)
+{
+	assert(battle_ok(bt));
+
+	battle_update_component(bt, ticks, BATTLE_COMPONENT_ALL);
+}
+
+void
+battle_state_menu_draw(const struct battle *bt)
+{
+	assert(battle_ok(bt));
+
+	battle_draw_component(bt, BATTLE_COMPONENT_ALL);
+}
+
+void
+battle_state_menu(struct battle *bt)
+{
+	assert(bt);
+
+	struct battle_state *state;
+
+	state = alloc_new0(sizeof (*state));
+	state->data = bt;
+	state->handle = handle;
+	state->update = update;
+	state->draw = draw;
+	state->finish = finish;
+
+	battle_bar_start(bt->bar, bt);
+	battle_switch(bt, state);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-rpg/mlk/rpg/battle-state-menu.h	Sat Oct 15 21:24:17 2022 +0200
@@ -0,0 +1,38 @@
+/*
+ * battle-state-menu.c -- battle state (menu)
+ *
+ * Copyright (c) 2020-2022 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 MLK_RPG_BATTLE_STATE_MENU_H
+#define MLK_RPG_BATTLE_STATE_MENU_H
+
+struct battle;
+
+union event;
+
+void
+battle_state_menu_handle(struct battle *, const union event *);
+
+void
+battle_state_menu_update(struct battle *, unsigned int);
+
+void
+battle_state_menu_draw(const struct battle *);
+
+void
+battle_state_menu(struct battle *);
+
+#endif /* !MLK_RPG_BATTLE_STATE_MENU_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-rpg/mlk/rpg/battle-state-opening.c	Sat Oct 15 21:24:17 2022 +0200
@@ -0,0 +1,111 @@
+/*
+ * battle-state-opening.c -- battle state (opening)
+ *
+ * Copyright (c) 2020-2022 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 <stdlib.h>
+
+#include <mlk/core/alloc.h>
+#include <mlk/core/painter.h>
+#include <mlk/core/panic.h>
+#include <mlk/core/window.h>
+
+#include "battle-state-check.h"
+#include "battle-state-opening.h"
+#include "battle-state.h"
+#include "battle.h"
+
+#define DELAY (1000U)
+
+struct self {
+	/* Always keep first. */
+	struct battle_state_opening data;
+	struct battle_state state;
+};
+
+static int
+update(struct battle_state *st, struct battle *bt, unsigned int ticks)
+{
+	(void)bt;
+
+	return battle_state_opening_update(st->data, bt, ticks);
+}
+
+static void
+draw(const struct battle_state *st, const struct battle *bt)
+{
+	(void)bt;
+
+	battle_state_opening_draw(st->data, bt);
+}
+
+static void
+finish(struct battle_state *st, struct battle *bt)
+{
+	(void)bt;
+
+	free(st->data);
+}
+
+int
+battle_state_opening_update(struct battle_state_opening *op, struct battle *bt, unsigned int ticks)
+{
+	op->elapsed += ticks;
+
+	/*
+	 * Those function will effectively change state accordingly to the
+	 * order of playing.
+	 */
+	if (op->elapsed >= DELAY)
+		battle_state_check(bt);
+
+	return 0;
+}
+
+void
+battle_state_opening_draw(const struct battle_state_opening *op, const struct battle *bt)
+{
+	assert(op);
+	assert(bt);
+
+	const unsigned int w = window.w;
+	const unsigned int h = window.h / 2;
+	const unsigned int ch = op->elapsed * h / DELAY;
+
+	battle_draw_component(bt, BATTLE_COMPONENT_BACKGROUND | BATTLE_COMPONENT_ENTITIES);
+
+	/* Draw some bezels opening. */
+	painter_set_color(0x000000ff);
+	painter_draw_rectangle(0, 0, w, h - ch);
+	painter_draw_rectangle(0, h + ch, w, h - ch);
+}
+
+void
+battle_state_opening(struct battle *bt)
+{
+	assert(bt);
+
+	struct self *self;
+
+	self = alloc_new0(sizeof (*self));
+	self->state.data = self;
+	self->state.update = update;
+	self->state.draw = draw;
+	self->state.finish = finish;
+
+	battle_switch(bt, &self->state);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-rpg/mlk/rpg/battle-state-opening.h	Sat Oct 15 21:24:17 2022 +0200
@@ -0,0 +1,37 @@
+/*
+ * battle-state-opening.h -- battle state (opening)
+ *
+ * Copyright (c) 2020-2022 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 MLK_RPG_BATTLE_STATE_OPENING_H
+#define MLK_RPG_BATTLE_STATE_OPENING_H
+
+struct battle;
+
+struct battle_state_opening {
+	unsigned int elapsed;
+};
+
+int
+battle_state_opening_update(struct battle_state_opening *, struct battle *, unsigned int);
+
+void
+battle_state_opening_draw(const struct battle_state_opening *, const struct battle *);
+
+void
+battle_state_opening(struct battle *);
+
+#endif /* !MLK_RPG_BATTLE_STATE_OPENING_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-rpg/mlk/rpg/battle-state-rendering.c	Sat Oct 15 21:24:17 2022 +0200
@@ -0,0 +1,114 @@
+/*
+ * battle-state-rendering.c -- battle state (rendering an action)
+ *
+ * Copyright (c) 2020-2022 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 <stdlib.h>
+
+#include <mlk/core/alloc.h>
+#include <mlk/core/drawable.h>
+
+#include "battle-state-rendering.h"
+#include "battle.h"
+
+struct self {
+	struct battle_state_rendering data;
+	struct battle_state state;
+};
+
+static int
+update(struct battle_state *st, struct battle *bt, unsigned int ticks)
+{
+	battle_state_rendering_update(st->data, bt, ticks);
+
+	return 0;
+}
+
+static void
+draw(const struct battle_state *st, const struct battle *bt)
+{
+	battle_state_rendering_draw(st->data, bt);
+}
+
+static void
+finish(struct battle_state *st, struct battle *bt)
+{
+	(void)bt;
+
+	battle_state_rendering_finish(st->data);
+	free(st->data);
+}
+
+void
+battle_state_rendering_init(struct battle_state_rendering *rdr, struct drawable *dw)
+{
+	assert(rdr);
+	assert(dw);
+
+	rdr->drawable = dw;
+}
+
+int
+battle_state_rendering_update(struct battle_state_rendering *rdr, struct battle *bt, unsigned int ticks)
+{
+	assert(rdr);
+	assert(battle_ok(bt));
+
+	battle_update_component(bt, BATTLE_COMPONENT_ALL, ticks);
+
+	if (drawable_update(rdr->drawable, ticks)) {
+		drawable_end(rdr->drawable);
+		return 1;
+	}
+
+	return 0;
+}
+
+void
+battle_state_rendering_draw(const struct battle_state_rendering *rdr, const struct battle *bt)
+{
+	assert(rdr);
+
+	battle_draw_component(bt, BATTLE_COMPONENT_ALL);
+	drawable_draw(rdr->drawable);
+}
+
+void
+battle_state_rendering_finish(struct battle_state_rendering *rdr)
+{
+	assert(rdr);
+
+	drawable_finish(rdr->drawable);
+}
+
+void
+battle_state_rendering(struct battle *bt, struct drawable *dw)
+{
+	assert(bt);
+	assert(dw);
+
+	struct self *self;
+
+	self = alloc_new0(sizeof (*self));
+	self->state.data = self;
+	self->state.update = update;
+	self->state.draw = draw;
+	self->state.finish = finish;
+
+	battle_state_rendering_init(&self->data, dw);
+	battle_switch(bt, &self->state);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-rpg/mlk/rpg/battle-state-rendering.h	Sat Oct 15 21:24:17 2022 +0200
@@ -0,0 +1,44 @@
+/*
+ * battle-state-rendering.h -- battle state (rendering an action)
+ *
+ * Copyright (c) 2020-2022 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 MLK_RPG_BATTLE_STATE_RENDERING_H
+#define MLK_RPG_BATTLE_STATE_RENDERING_H
+
+struct battle;
+struct drawable;
+
+struct battle_state_rendering {
+	struct drawable *drawable;
+};
+
+void
+battle_state_rendering_init(struct battle_state_rendering *, struct drawable *);
+
+int
+battle_state_rendering_update(struct battle_state_rendering *, struct battle *, unsigned int);
+
+void
+battle_state_rendering_draw(const struct battle_state_rendering *, const struct battle *);
+
+void
+battle_state_rendering_finish(struct battle_state_rendering *);
+
+void
+battle_state_rendering(struct battle *, struct drawable *);
+
+#endif /* !MLK_RPG_BATTLE_STATE_RENDERING_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-rpg/mlk/rpg/battle-state-selection.c	Sat Oct 15 21:24:17 2022 +0200
@@ -0,0 +1,231 @@
+/*
+ * battle-state-selection.c -- battle state (selection)
+ *
+ * Copyright (c) 2020-2022 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 <stdlib.h>
+#include <string.h>
+
+#include <mlk/core/alloc.h>
+#include <mlk/core/event.h>
+#include <mlk/core/panic.h>
+#include <mlk/core/sprite.h>
+#include <mlk/core/util.h>
+
+#include <mlk/ui/theme.h>
+
+#include "battle-bar.h"
+#include "battle-state-item.h"
+#include "battle-state-menu.h"
+#include "battle-state-selection.h"
+#include "battle-state.h"
+#include "battle.h"
+#include "character.h"
+#include "inventory.h"
+#include "selection.h"
+#include "spell.h"
+
+struct self {
+	struct battle_state_selection data;
+	struct battle_state state;
+};
+
+static void
+select_adj_in(struct battle_state_selection *slt, struct battle_entity **entities, size_t entitiesz, int step)
+{
+	assert(slt->select.index_character != (unsigned int)-1);
+
+	unsigned int newselection = slt->select.index_character;
+
+	if (step < 0) {
+		while (newselection > 0) {
+			if (character_ok(entities[--newselection]->ch)) {
+				slt->select.index_character = newselection;
+				break;
+			}
+		}
+	} else {
+		while (newselection < entitiesz) {
+			if (character_ok(entities[++newselection]->ch)) {
+				slt->select.index_character = newselection;
+				break;
+			}
+		}
+	}
+}
+
+static void
+select_adj(struct battle_state_selection *slt, const struct battle *bt, int step)
+{
+	if (slt->select.index_side == 0)
+		select_adj_in(slt, bt->enemies, bt->enemiesz, step);
+	else
+		select_adj_in(slt, bt->team, bt->teamsz, step);
+}
+
+static void
+handle_keydown(struct battle_state_selection *stl, struct battle *bt, const union event *ev)
+{
+	assert(ev->type == EVENT_KEYDOWN);
+
+	switch (ev->key.key) {
+	case KEY_ESCAPE:
+		battle_state_menu(bt);
+		break;
+	case KEY_ENTER:
+		battle_bar_select(bt->bar, bt, &stl->select);
+		break;
+	case KEY_LEFT:
+		if (stl->select.allowed_sides & SELECTION_SIDE_ENEMY)
+			stl->select.index_side = 0;
+		break;
+	case KEY_RIGHT:
+		if (stl->select.allowed_sides & SELECTION_SIDE_TEAM)
+			stl->select.index_side = 1;
+		break;
+	case KEY_UP:
+		select_adj(stl, bt, -1);
+		break;
+	case KEY_DOWN:
+		select_adj(stl, bt, +1);
+		break;
+	case KEY_TAB:
+		if (stl->select.allowed_kinds == SELECTION_KIND_BOTH)
+			stl->select.index_character = -1;
+		break;
+	default:
+		break;
+	}
+}
+
+static void
+draw_cursor(const struct battle *bt, const struct battle_entity *et)
+{
+	const struct theme *theme = BATTLE_THEME(bt);
+	const struct sprite *cursor = theme->sprites[THEME_SPRITE_CURSOR];
+	int x, y;
+	unsigned int lh;
+
+	if (!cursor)
+		return;
+
+	label_query(&et->name, NULL, &lh);
+
+	x = et->name.x - cursor->cellw - theme->padding;
+	y = et->name.y + (((int)(lh) - (int)(cursor->cellh)) / 2);
+
+	sprite_draw(cursor, 1, 2, x, y);
+}
+
+static void
+draw_cursors(const struct battle *bt,
+             struct battle_entity * const *entities,
+             size_t entitiesz)
+{
+	for (size_t i = 0; i < entitiesz; ++i) {
+		const struct battle_entity *et = entities[i];
+
+		if (et && character_ok(et->ch))
+			draw_cursor(bt, et);
+	}
+}
+
+static void
+handle(struct battle_state *st, struct battle *bt, const union event *ev)
+{
+	battle_state_selection_handle(st->data, bt, ev);
+}
+
+static void
+draw(const struct battle_state *st, const struct battle *bt)
+{
+	battle_state_selection_draw(st->data, bt);
+}
+
+static void
+finish(struct battle_state *st, struct battle *bt)
+{
+	(void)bt;
+
+	free(st->data);
+}
+
+void
+battle_state_selection_init(struct battle_state_selection *stl, const struct selection *select)
+{
+	assert(stl);
+	assert(select);
+
+	memcpy(&stl->select, select, sizeof (*select));
+}
+
+void
+battle_state_selection_handle(struct battle_state_selection *stl, struct battle *bt, const union event *ev)
+{
+	assert(stl);
+	assert(bt);
+	assert(ev);
+
+	switch (ev->type) {
+	case EVENT_KEYDOWN:
+		handle_keydown(stl, bt, ev);
+		break;
+	default:
+		break;
+	}
+}
+
+void
+battle_state_selection_draw(const struct battle_state_selection *stl, const struct battle *bt)
+{
+	assert(stl);
+	assert(bt);
+
+	battle_draw_component(bt, BATTLE_COMPONENT_ALL);
+
+	if (stl->select.index_character == -1U) {
+		/* All selected. */
+		if (stl->select.index_side == 0)
+			draw_cursors(bt, bt->enemies, bt->enemiesz);
+		else
+			draw_cursors(bt, bt->team, bt->teamsz);
+	} else {
+		/* Select one. */
+		if (stl->select.index_side == 0)
+			draw_cursor(bt, bt->enemies[stl->select.index_character]);
+		else
+			draw_cursor(bt, bt->team[stl->select.index_character]);
+	}
+}
+
+void
+battle_state_selection(struct battle *bt, const struct selection *select)
+{
+	assert(bt);
+	assert(select);
+
+	struct self *self;
+
+	self = alloc_new0(sizeof (*self));
+	self->state.data = self;
+	self->state.handle = handle;
+	self->state.draw = draw;
+	self->state.finish = finish;
+
+	battle_state_selection_init(&self->data, select);
+	battle_switch(bt, &self->state);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-rpg/mlk/rpg/battle-state-selection.h	Sat Oct 15 21:24:17 2022 +0200
@@ -0,0 +1,44 @@
+/*
+ * battle-state-selection.h -- battle state (selection)
+ *
+ * Copyright (c) 2020-2022 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 MLK_RPG_BATTLE_STATE_SELECTION_H
+#define MLK_RPG_BATTLE_STATE_SELECTION_H
+
+#include <mlk/rpg/selection.h>
+
+struct battle;
+
+union event;
+
+struct battle_state_selection {
+	struct selection select;
+};
+
+void
+battle_state_selection_init(struct battle_state_selection *, const struct selection *);
+
+void
+battle_state_selection_handle(struct battle_state_selection *, struct battle *, const union event *);
+
+void
+battle_state_selection_draw(const struct battle_state_selection *, const struct battle *);
+
+void
+battle_state_selection(struct battle *, const struct selection *);
+
+#endif /* !MLK_RPG_BATTLE_STATE_SELECTION_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-rpg/mlk/rpg/battle-state-victory.c	Sat Oct 15 21:24:17 2022 +0200
@@ -0,0 +1,136 @@
+/*
+ * battle-state-victory.c -- battle state (victory)
+ *
+ * Copyright (c) 2020-2022 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 <mlk/core/alloc.h>
+#include <mlk/core/music.h>
+#include <mlk/core/panic.h>
+#include <mlk/core/window.h>
+
+#include "battle-state-closing.h"
+#include "battle-state-victory.h"
+#include "battle-state.h"
+#include "battle.h"
+
+struct self {
+	struct battle_state_victory data;
+	struct battle_state state;
+};
+
+static void
+handle(struct battle_state *st, struct battle *bt, const union event *ev)
+{
+	(void)bt;
+
+	battle_state_victory_handle(st->data, ev);
+}
+
+static int
+update(struct battle_state *st, struct battle *bt, unsigned int ticks)
+{
+	(void)bt;
+
+	return battle_state_victory_update(st->data, bt, ticks);
+}
+
+static void
+draw(const struct battle_state *st, const struct battle *bt)
+{
+	(void)bt;
+
+	battle_state_victory_draw(st->data, bt);
+}
+
+void
+battle_state_victory_init(struct battle_state_victory *vic, struct battle *bt)
+{
+	assert(bt);
+
+	vic->text = "Victory!";
+
+	vic->msg.lines = &vic->text;
+	vic->msg.linesz = 1;
+	vic->msg.theme = bt->theme;
+	vic->msg.flags = MESSAGE_FLAGS_AUTOMATIC |
+	                 MESSAGE_FLAGS_FADEIN |
+	                 MESSAGE_FLAGS_FADEOUT;
+	vic->msg.timeout = MESSAGE_TIMEOUT_DEFAULT;
+	vic->msg.delay = MESSAGE_DELAY_DEFAULT;
+
+	message_start(&vic->msg);
+	message_query(&vic->msg, NULL, &vic->msg.h);
+
+	vic->msg.w = window.w * 0.6;
+	vic->msg.y = window.h * 0.1;
+	vic->msg.x = (window.w - vic->msg.w) / 2;
+
+	bt->status = BATTLE_STATUS_WON;
+
+	if (bt->music[1])
+		music_play(bt->music[1], MUSIC_NONE);
+}
+
+void
+battle_state_victory_handle(struct battle_state_victory *vic, const union event *ev)
+{
+	assert(vic);
+
+	message_handle(&vic->msg, ev);
+}
+
+int
+battle_state_victory_update(struct battle_state_victory *vic, struct battle *bt, unsigned int ticks)
+{
+	assert(vic);
+	assert(bt);
+
+	battle_update_component(bt, BATTLE_COMPONENT_ALL, ticks);
+
+	if (message_update(&vic->msg, ticks))
+		battle_state_closing(bt);
+
+	return 0;
+}
+
+void
+battle_state_victory_draw(const struct battle_state_victory *vic, const struct battle *bt)
+{
+	assert(vic);
+
+	battle_draw_component(bt, BATTLE_COMPONENT_ALL);
+	message_draw(&vic->msg);
+}
+
+void
+battle_state_victory(struct battle *bt)
+{
+	assert(bt);
+
+	struct self *self;
+
+	/* TODO: compute money, xp and drop. */
+	self = alloc_new0(sizeof (*self));
+	self->state.data = self;
+	self->state.handle = handle;
+	self->state.update = update;
+	self->state.draw = draw;
+
+	battle_state_victory_init(&self->data, bt);
+	battle_switch(bt, &self->state);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-rpg/mlk/rpg/battle-state-victory.h	Sat Oct 15 21:24:17 2022 +0200
@@ -0,0 +1,48 @@
+/*
+ * battle-state-victory.h -- battle state (victory)
+ *
+ * Copyright (c) 2020-2022 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 MLK_RPG_BATTLE_STATE_VICTORY_H
+#define MLK_RPG_BATTLE_STATE_VICTORY_H
+
+#include <mlk/rpg/message.h>
+
+struct battle;
+
+union event;
+
+struct battle_state_victory {
+	const char *text;
+	struct message msg;
+};
+
+void
+battle_state_victory_init(struct battle_state_victory *, struct battle *);
+
+void
+battle_state_victory_handle(struct battle_state_victory *, const union event *);
+
+int
+battle_state_victory_update(struct battle_state_victory *, struct battle *, unsigned int);
+
+void
+battle_state_victory_draw(const struct battle_state_victory *, const struct battle *);
+
+void
+battle_state_victory(struct battle *);
+
+#endif /* !MLK_RPG_BATTLE_STATE_VICTORY_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-rpg/mlk/rpg/battle-state.c	Sat Oct 15 21:24:17 2022 +0200
@@ -0,0 +1,64 @@
+/*
+ * battle-state.c -- battle abstract state
+ *
+ * Copyright (c) 2020-2022 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 "battle-state.h"
+
+void
+battle_state_handle(struct battle_state *st, struct battle *bt, const union event *ev)
+{
+	assert(st);
+	assert(bt);
+	assert(ev);
+
+	if (st->handle)
+		st->handle(st, bt, ev);
+}
+
+int
+battle_state_update(struct battle_state *st, struct battle *bt, unsigned int ticks)
+{
+	assert(st);
+	assert(bt);
+
+	if (st->update)
+		return st->update(st, bt, ticks);
+
+	return 0;
+}
+
+void
+battle_state_draw(const struct battle_state *st, const struct battle *bt)
+{
+	assert(st);
+	assert(bt);
+
+	if (st->draw)
+		st->draw(st, bt);
+}
+
+void
+battle_state_finish(struct battle_state *st, struct battle *bt)
+{
+	assert(st);
+	assert(bt);
+
+	if (st->finish)
+		st->finish(st, bt);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-rpg/mlk/rpg/battle-state.h	Sat Oct 15 21:24:17 2022 +0200
@@ -0,0 +1,55 @@
+/*
+ * battle-state.h -- battle abstract state
+ *
+ * Copyright (c) 2020-2022 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 MLK_RPG_BATTLE_STATE_H
+#define MLK_RPG_BATTLE_STATE_H
+
+#include <mlk/core/core.h>
+
+struct battle;
+struct character;
+struct inventory_slot;
+struct selection;
+
+union event;
+
+struct battle_state {
+	void *data;
+	void (*handle)(struct battle_state *, struct battle *, const union event *);
+	int (*update)(struct battle_state *, struct battle *, unsigned int);
+	void (*draw)(const struct battle_state *, const struct battle *);
+	void (*finish)(struct battle_state *, struct battle *);
+};
+
+CORE_BEGIN_DECLS
+
+void
+battle_state_handle(struct battle_state *, struct battle *, const union event *);
+
+int
+battle_state_update(struct battle_state *, struct battle *, unsigned int);
+
+void
+battle_state_draw(const struct battle_state *, const struct battle *);
+
+void
+battle_state_finish(struct battle_state *, struct battle *);
+
+CORE_END_DECLS
+
+#endif /* !MLK_RPG_BATTLE_STATE_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-rpg/mlk/rpg/battle.c	Sat Oct 15 21:24:17 2022 +0200
@@ -0,0 +1,502 @@
+/*
+ * battle.c -- battles
+ *
+ * Copyright (c) 2020-2022 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 <stdlib.h>
+#include <string.h>
+
+#include <mlk/core/alloc.h>
+#include <mlk/core/event.h>
+#include <mlk/core/font.h>
+#include <mlk/core/music.h>
+#include <mlk/core/sprite.h>
+#include <mlk/core/texture.h>
+#include <mlk/core/trace.h>
+#include <mlk/core/util.h>
+#include <mlk/core/window.h>
+
+#include <mlk/ui/align.h>
+#include <mlk/ui/frame.h>
+#include <mlk/ui/label.h>
+#include <mlk/ui/theme.h>
+
+#include "battle-bar.h"
+#include "battle-indicator.h"
+#include "battle-state-ai.h"
+#include "battle-state-attacking.h"
+#include "battle-state-check.h"
+#include "battle-state-menu.h"
+#include "battle-state-opening.h"
+#include "battle-state.h"
+#include "battle.h"
+#include "character.h"
+#include "inventory.h"
+#include "item.h"
+#include "spell.h"
+
+struct indicator {
+	struct drawable dw;
+	struct battle_indicator bti;
+};
+
+static int
+indicator_update(struct drawable *dw, unsigned int ticks)
+{
+	struct indicator *id = dw->data;
+
+	return battle_indicator_update(&id->bti, ticks);
+}
+
+static void
+indicator_draw(struct drawable *dw)
+{
+	const struct indicator *id = dw->data;
+
+	battle_indicator_draw(&id->bti, dw->x, dw->y);
+}
+
+static void
+indicator_free(struct drawable *dw)
+{
+	struct indicator *id = dw->data;
+
+	battle_indicator_finish(&id->bti);
+	free(id);
+}
+
+static struct battle_entity *
+find(struct battle *bt, const struct character *ch)
+{
+	struct battle_entity *et;
+
+	BATTLE_TEAM_FOREACH(bt, et)
+		if (et->ch == ch)
+			return et;
+	BATTLE_ENEMY_FOREACH(bt, et)
+		if (et->ch == ch)
+			return et;
+
+	return NULL;
+}
+
+static struct battle_entity *
+random_select(struct battle_entity **group, size_t groupsz)
+{
+	struct battle_entity *ret = NULL, *et = NULL;
+
+	do {
+		et = group[util_nrand(0, groupsz - 1)];
+
+		if (et && et->ch)
+			ret = et;
+	} while (!ret);
+
+	return ret;
+}
+
+static int
+cmp_order(const void *d1, const void *d2)
+{
+	const struct battle_entity *et1 = *(const struct battle_entity **)d1;
+	const struct battle_entity *et2 = *(const struct battle_entity **)d2;
+
+	return et2->ch->agt < et1->ch->agt;
+}
+
+static int
+is_team(const struct battle *bt, const struct character *ch)
+{
+	for (size_t i = 0; i < bt->teamsz; ++i)
+		if (bt->team[i] && bt->team[i]->ch == ch)
+			return 1;
+
+	return 0;
+}
+
+static void
+positionate_name(struct battle_entity *et, const struct battle *bt)
+{
+	unsigned int lw;
+	struct sprite *sprite;
+
+	/* Show the character name below its sprite. */
+	sprite = et->ch->sprites[CHARACTER_SPRITE_NORMAL];
+
+	et->name.text = et->ch->name;
+	et->name.flags = LABEL_FLAGS_SHADOW;
+	label_query(&et->name, &lw, NULL);
+	et->name.y = et->y + sprite->cellh + BATTLE_THEME(bt)->padding;
+	et->name.x = et->x + (sprite->cellw / 2) - (lw / 2);
+}
+
+static void
+positionate_names(struct battle *bt)
+{
+	struct battle_entity *et;
+
+	BATTLE_TEAM_FOREACH(bt, et)
+		if (character_ok(et->ch))
+			positionate_name(et, bt);
+	BATTLE_ENEMY_FOREACH(bt, et)
+		if (character_ok(et->ch))
+			positionate_name(et, bt);
+}
+
+static void
+positionate_team(struct battle *bt)
+{
+	struct battle_entity *et;
+	unsigned int requirement = 0, nmemb = 0, spacing;
+	int x, y;
+
+	BATTLE_TEAM_FOREACH(bt, et) {
+		/* Stop in case any member of the team has been positionated. */
+		if (et->x != 0 || et->y != 0)
+			return;
+
+		if (battle_entity_ok(bt->team[i])) {
+			nmemb++;
+			requirement += et->ch->sprites[CHARACTER_SPRITE_NORMAL]->cellh;
+		}
+	}
+
+	/* TODO: compute a correct x placement. */
+	spacing = (window.h - requirement) / (nmemb + 1);
+	x = window.w - 200;
+	y = spacing;
+
+	BATTLE_TEAM_FOREACH(bt, et) {
+		if (battle_entity_ok(et)) {
+			et->x = x;
+			et->y = y;
+			y += et->ch->sprites[CHARACTER_SPRITE_NORMAL]->cellh + spacing;
+		}
+	}
+}
+
+static void
+draw_entities(const struct battle *bt, struct battle_entity **entities, size_t entitiesz)
+{
+	for (size_t i = 0; i < entitiesz; ++i) {
+		if (battle_entity_ok(entities[i]))
+			battle_entity_draw(entities[i], bt);
+	}
+}
+
+static void
+update_entities(struct battle_entity **entities, size_t entitiesz, unsigned int ticks)
+{
+	for (size_t i = 0; i < entitiesz; ++i) {
+		if (battle_entity_ok(entities[i]))
+			battle_entity_update(entities[i], ticks);
+	}
+}
+
+void
+battle_init(struct battle *bt)
+{
+	assert(bt);
+
+	memset(bt, 0, sizeof (*bt));
+}
+
+int
+battle_ok(const struct battle *bt)
+{
+	return bt && bt->state && bt->bar && bt->enemiesz && bt->team;
+}
+
+void
+battle_start(struct battle *bt)
+{
+	assert(bt);
+
+	struct battle_entity *et;
+
+	BATTLE_TEAM_FOREACH(bt, et)
+		if (battle_entity_ok(et))
+			battle_entity_init(et);
+	BATTLE_ENEMY_FOREACH(bt, et)
+		if (battle_entity_ok(et))
+			battle_entity_init(et);
+
+	positionate_team(bt);
+	positionate_names(bt);
+
+	/* Start the state "opening" animation. */
+	battle_state_opening(bt);
+
+	/* Play music if present. */
+	if (bt->music[0])
+		music_play(bt->music[0], MUSIC_LOOP);
+
+	battle_order(bt);
+}
+
+void
+battle_switch(struct battle *bt, struct battle_state *st)
+{
+	assert(bt);
+	assert(st);
+
+	if (bt->state)
+		battle_state_finish(bt->state, bt);
+
+	bt->state = st;
+}
+
+void
+battle_order(struct battle *bt)
+{
+	struct battle_entity **porder;
+
+	/* Create a pointer list to every entity. */
+	bt->order = alloc_rearray0(bt->order, bt->ordersz,
+	    bt->teamsz + bt->enemiesz, sizeof (*bt->order));
+	bt->ordersz = bt->teamsz + bt->enemiesz;
+	bt->ordercur = porder = bt->order;
+
+	for (size_t i = 0; i < bt->teamsz; ++i)
+		if (bt->team[i] && character_ok(bt->team[i]->ch))
+			*porder++ = bt->team[i];
+	for (size_t i = 0; i < bt->enemiesz; ++i)
+		if (bt->enemies[i] && character_ok(bt->enemies[i]->ch))
+			*porder++ = bt->enemies[i];
+
+	/* Now sort. */
+	qsort(bt->order, bt->ordersz, sizeof (*bt->order), cmp_order);
+}
+
+struct battle_entity *
+battle_current(const struct battle *bt)
+{
+	assert(bt);
+
+	return *bt->ordercur;
+}
+
+size_t
+battle_index(const struct battle *bt)
+{
+	assert(bt);
+
+	return bt->ordercur - bt->order;
+}
+
+void
+battle_attack(struct battle *bt,
+              struct character *source,
+              struct character *target)
+{
+	assert(bt);
+	assert(character_ok(source));
+
+	/* Target is empty? select randomly. */
+	if (!target) {
+		if (is_team(bt, source))
+			target = random_select(bt->enemies, bt->enemiesz)->ch;
+		else
+			target = random_select(bt->team, bt->teamsz)->ch;
+	}
+
+	battle_state_attacking(battle_find(bt, source), battle_find(bt, target), bt);
+}
+
+void
+battle_cast(struct battle *bt,
+            struct character *source,
+            const struct spell *spell,
+            const struct selection *selection)
+{
+	assert(bt);
+	assert(source);
+	assert(spell);
+	assert((unsigned int)source->mp >= spell->mp);
+
+	/* TODO: animate. */
+	source->mp -= spell->mp;
+	spell_action(spell, bt, source, selection);
+}
+
+void
+battle_use(struct battle *bt, const struct item *item, struct character *owner, struct character *target)
+{
+	assert(bt);
+	assert(item);
+	assert(owner);
+	assert(target);
+
+	/*
+	 * Change the state to check prior to execute the item so it can change to something else
+	 * if needed.
+	 */
+	battle_state_check(bt);
+
+	inventory_consume(bt->inventory, item, 1);
+	item_exec_battle(item, bt, owner, target);
+}
+
+void
+battle_next(struct battle *bt)
+{
+	assert(bt);
+
+	if (!bt->ordercur)
+		battle_order(bt);
+	else {
+		if (bt->ordercur - bt->order + (size_t)1U >= bt->ordersz)
+			battle_order(bt);
+		else
+			bt->ordercur++;
+	}
+
+	if (is_team(bt, battle_current(bt)->ch)) {
+		battle_bar_start(bt->bar, bt);
+		battle_state_menu(bt);
+	} else
+		battle_state_ai(bt);
+}
+
+struct battle_entity *
+battle_find(struct battle *bt, const struct character *ch)
+{
+	assert(bt);
+
+	return find(bt, ch);
+}
+
+void
+battle_indicator_hp(struct battle *bt, const struct character *target, long amount)
+{
+	assert(bt);
+	assert(target);
+
+	const struct battle_entity *et = find(bt, target);
+	struct indicator *id;
+
+	if (!(bt->effects)) {
+		tracef("unable to add id without a drawable_stack");
+		return;
+	}
+
+	id = alloc_new0(sizeof (*id));
+	id->bti.color = BATTLE_INDICATOR_HP_COLOR;
+	id->bti.amount = labs(amount);
+
+	/* TODO: positionate better. */
+	id->dw.x = et->x + target->sprites[CHARACTER_SPRITE_NORMAL]->cellw;
+	id->dw.y = et->y + target->sprites[CHARACTER_SPRITE_NORMAL]->cellh;
+	id->dw.data = id;
+	id->dw.update = indicator_update;
+	id->dw.draw = indicator_draw;
+	id->dw.finish = indicator_free;
+
+	battle_indicator_start(&id->bti);
+
+	if (drawable_stack_add(bt->effects, &id->dw) < 0)
+		drawable_finish(&id->dw);
+}
+
+void
+battle_handle_component(struct battle *bt, const union event *ev, enum battle_component comp)
+{
+	assert(bt);
+	assert(ev);
+
+	if (comp & BATTLE_COMPONENT_BAR)
+		battle_bar_handle(bt->bar, bt, ev);
+	if ((comp & BATTLE_COMPONENT_ACTIONS) && bt->actions)
+		action_stack_handle(bt->actions, ev);
+}
+
+void
+battle_handle(struct battle *bt, const union event *ev)
+{
+	assert(bt);
+	assert(ev);
+
+	battle_state_handle(bt->state, bt, ev);
+}
+
+void
+battle_update_component(struct battle *bt, unsigned int ticks, enum battle_component comp)
+{
+	assert(bt);
+
+	if (comp & BATTLE_COMPONENT_ENTITIES) {
+		update_entities(bt->team, bt->teamsz, ticks);
+		update_entities(bt->enemies, bt->enemiesz, ticks);
+	}
+	if (comp & BATTLE_COMPONENT_BAR)
+		battle_bar_update(bt->bar, bt, ticks);
+	if ((comp & BATTLE_COMPONENT_ACTIONS) && bt->actions)
+		action_stack_update(bt->actions, ticks);
+	if ((comp & BATTLE_COMPONENT_DRAWABLES) && bt->effects)
+		drawable_stack_update(bt->effects, ticks);
+}
+
+int
+battle_update(struct battle *bt, unsigned int ticks)
+{
+	assert(bt && bt->state);
+
+	return battle_state_update(bt->state, bt, ticks);
+}
+
+void
+battle_draw_component(const struct battle *bt, enum battle_component comp)
+{
+	assert(bt);
+
+	if ((comp & BATTLE_COMPONENT_BACKGROUND) && texture_ok(bt->background))
+		texture_scale(bt->background,
+		    0, 0, bt->background->w, bt->background->h,
+		    0, 0, window.w, window.h,
+		    0.f);
+	if (comp & BATTLE_COMPONENT_ENTITIES) {
+		draw_entities(bt, bt->team, bt->teamsz);
+		draw_entities(bt, bt->enemies, bt->enemiesz);
+	}
+	if (comp & BATTLE_COMPONENT_BAR)
+		battle_bar_draw(bt->bar, bt);
+	if ((comp & BATTLE_COMPONENT_ACTIONS) && bt->actions)
+		action_stack_draw(bt->actions);
+	if ((comp & BATTLE_COMPONENT_DRAWABLES) && bt->effects)
+		drawable_stack_draw(bt->effects);
+}
+
+void
+battle_draw(const struct battle *bt)
+{
+	assert(battle_ok(bt));
+
+	battle_state_draw(bt->state, bt);
+}
+
+void
+battle_finish(struct battle *bt)
+{
+	assert(bt);
+
+	if (bt->state)
+		battle_state_finish(bt->state, bt);
+
+	free(bt->order);
+	memset(bt, 0, sizeof (*bt));
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-rpg/mlk/rpg/battle.h	Sat Oct 15 21:24:17 2022 +0200
@@ -0,0 +1,153 @@
+/*
+ * battle.h -- battles
+ *
+ * Copyright (c) 2020-2022 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 MLK_RPG_BATTLE_H
+#define MLK_RPG_BATTLE_H
+
+#include <mlk/core/action.h>
+#include <mlk/core/action-stack.h>
+#include <mlk/core/core.h>
+#include <mlk/core/drawable.h>
+#include <mlk/core/drawable-stack.h>
+
+#include <mlk/ui/frame.h>
+#include <mlk/ui/gridmenu.h>
+
+#include "battle-entity.h"
+#include "battle-state.h"
+#include "selection.h"
+#include "spell.h"
+
+union event;
+
+struct battle_bar;
+struct character;
+struct inventory;
+struct item;
+struct music;
+struct selection;
+struct spell;
+struct theme;
+
+#define BATTLE_TEAM_FOREACH(bt, iter) \
+	for (size_t i = 0; i < (bt)->teamsz && ((iter) = (bt)->team[i]); ++i)
+#define BATTLE_ENEMY_FOREACH(bt, iter) \
+	for (size_t i = 0; i < (bt)->enemiesz && ((iter) = (bt)->enemies[i]); ++i)
+
+#define BATTLE_THEME(bt) ((bt)->theme ? (bt)->theme : theme_default())
+
+enum battle_status {
+	BATTLE_STATUS_NONE,
+	BATTLE_STATUS_RUNNING,
+	BATTLE_STATUS_WON,
+	BATTLE_STATUS_LOST,
+};
+
+enum battle_component {
+	BATTLE_COMPONENT_BACKGROUND     = (1 << 0),
+	BATTLE_COMPONENT_ENTITIES       = (1 << 1),
+	BATTLE_COMPONENT_BAR            = (1 << 2),
+	BATTLE_COMPONENT_ACTIONS        = (1 << 3),
+	BATTLE_COMPONENT_DRAWABLES      = (1 << 4),
+	BATTLE_COMPONENT_ALL            = 0xff
+};
+
+struct battle {
+	struct battle_state *state;
+	enum battle_status status;
+	struct battle_entity **team;
+	size_t teamsz;
+	struct battle_entity **enemies;
+	size_t enemiesz;
+	struct battle_entity **order;
+	struct battle_entity **ordercur;
+	size_t ordersz;
+	struct texture *background;
+	struct music *music[3];
+	struct theme *theme;
+	struct drawable_stack *effects;
+	struct action_stack *actions;
+	struct inventory *inventory;
+	struct battle_bar *bar;
+};
+
+CORE_BEGIN_DECLS
+
+void
+battle_init(struct battle *);
+
+int
+battle_ok(const struct battle *);
+
+void
+battle_start(struct battle *);
+
+void
+battle_next(struct battle *);
+
+struct battle_entity *
+battle_find(struct battle *, const struct character *);
+
+void
+battle_switch(struct battle *, struct battle_state *);
+
+void
+battle_order(struct battle *);
+
+struct battle_entity *
+battle_current(const struct battle *);
+
+size_t
+battle_index(const struct battle *);
+
+void
+battle_attack(struct battle *, struct character *, struct character *);
+
+void
+battle_cast(struct battle *, struct character *, const struct spell *, const struct selection *);
+
+void
+battle_use(struct battle *, const struct item *, struct character *, struct character *);
+
+void
+battle_indicator_hp(struct battle *, const struct character *, long);
+
+void
+battle_handle_component(struct battle *, const union event *, enum battle_component);
+
+void
+battle_handle(struct battle *, const union event *);
+
+void
+battle_update_component(struct battle *, unsigned int, enum battle_component);
+
+int
+battle_update(struct battle *, unsigned int);
+
+void
+battle_draw_component(const struct battle *, enum battle_component);
+
+void
+battle_draw(const struct battle *);
+
+void
+battle_finish(struct battle *);
+
+CORE_END_DECLS
+
+#endif /* MLK_RPG_BATTLE_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-rpg/mlk/rpg/character.c	Sat Oct 15 21:24:17 2022 +0200
@@ -0,0 +1,122 @@
+/*
+ * character.c -- character definition
+ *
+ * Copyright (c) 2020-2022 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 <mlk/core/sprite.h>
+
+#include <assets/sql/character-save.h>
+#include <assets/sql/character-load.h>
+
+#include "character.h"
+#include "equipment.h"
+#include "save.h"
+
+int
+character_ok(const struct character *ch)
+{
+	return ch && ch->name && ch->reset && sprite_ok(ch->sprites[CHARACTER_SPRITE_NORMAL]);
+}
+
+const char *
+character_status_string(enum character_status status)
+{
+	/*
+	 * We expect the user to only specify one status as character_status
+	 * is a bitmask.
+	 */
+	switch (status) {
+	case CHARACTER_STATUS_POISON:
+		return "poison";
+	default:
+		return "normal";
+	}
+}
+
+void
+character_reset(struct character *ch)
+{
+	assert(character_ok(ch));
+
+	ch->reset(ch);
+
+	/* For all equipments, apply its equip function. */
+	for (int i = 0; i < CHARACTER_EQUIPMENT_NUM; ++i)
+		if (ch->equipments[i])
+			equipment_equip(ch->equipments[i], ch);
+}
+
+void
+character_exec(struct character *ch, struct battle *bt)
+{
+	assert(character_ok(ch));
+
+	if (ch->exec)
+		ch->exec(ch, bt);
+}
+
+int
+character_save(const struct character *ch, struct save *s)
+{
+	assert(ch);
+	assert(save_ok(s));
+
+	return save_exec(s, (const char *)assets_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
+	);
+}
+
+int
+character_load(struct character *ch, struct save *s)
+{
+	assert(ch);
+	assert(save_ok(s));
+
+	struct save_stmt stmt;
+	enum save_stmt_errno ret;
+
+	if (save_stmt_init(&stmt, s, (const char *)assets_character_load, "s", ch->name) < 0)
+		return -1;
+
+	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_ROW;
+
+	save_stmt_finish(&stmt);
+
+	return ret ? 0 : -1;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-rpg/mlk/rpg/character.h	Sat Oct 15 21:24:17 2022 +0200
@@ -0,0 +1,109 @@
+/*
+ * character.h -- character definition
+ *
+ * Copyright (c) 2020-2022 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 MLK_RPG_CHARACTER_H
+#define MLK_RPG_CHARACTER_H
+
+#include <mlk/core/core.h>
+
+#define CHARACTER_SPELL_MAX (64)
+
+struct battle;
+struct save;
+struct sprite;
+struct spell;
+
+enum character_status {
+	CHARACTER_STATUS_NORMAL,
+	CHARACTER_STATUS_POISON         = (1 << 0)
+};
+
+enum character_sprite {
+	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
+};
+
+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;
+	enum character_status status;
+	unsigned int level;
+	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;
+
+	struct sprite *sprites[CHARACTER_SPRITE_NUM];
+	const struct equipment *equipments[CHARACTER_EQUIPMENT_NUM];
+	const struct spell *spells[CHARACTER_SPELL_MAX];
+
+	void (*reset)(struct character *owner);
+	void (*exec)(struct character *owner, struct battle *bt);
+};
+
+CORE_BEGIN_DECLS
+
+int
+character_ok(const struct character *ch);
+
+const char *
+character_status_string(enum character_status status);
+
+void
+character_reset(struct character *ch);
+
+void
+character_exec(struct character *ch, struct battle *bt);
+
+int
+character_save(const struct character *ch, struct save *s);
+
+int
+character_load(struct character *, struct save *);
+
+CORE_END_DECLS
+
+#endif /* !MLK_RPG_CHARACTER_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-rpg/mlk/rpg/equipment.c	Sat Oct 15 21:24:17 2022 +0200
@@ -0,0 +1,38 @@
+/*
+ * equipment.h -- character equipment
+ *
+ * Copyright (c) 2020-2022 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 "character.h"
+#include "equipment.h"
+
+int
+equipment_ok(const struct equipment *eq)
+{
+	return eq && eq->name && eq->description;
+}
+
+void
+equipment_equip(const struct equipment *eq, struct character *ch)
+{
+	assert(equipment_ok(eq));
+	assert(character_ok(ch));
+
+	if (eq->equip)
+		eq->equip(eq, ch);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-rpg/mlk/rpg/equipment.h	Sat Oct 15 21:24:17 2022 +0200
@@ -0,0 +1,66 @@
+/*
+ * equipment.h -- character equipment
+ *
+ * Copyright (c) 2020-2022 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 MLK_RPG_EQUIPMENT_H
+#define MLK_RPG_EQUIPMENT_H
+
+#include <mlk/core/core.h>
+
+struct character;
+struct texture;
+
+enum equipment_type {
+	/* Attack weapons. */
+	EQUIPMENT_TYPE_AXE,
+	EQUIPMENT_TYPE_BOW,
+	EQUIPMENT_TYPE_CROSSBOW,
+	EQUIPMENT_TYPE_DAGGER,
+	EQUIPMENT_TYPE_HAMMER,
+	EQUIPMENT_TYPE_SPIKE,
+	EQUIPMENT_TYPE_SWORD,
+	EQUIPMENT_TYPE_WAND,
+
+	/* Defense equipment. */
+	EQUIPMENT_TYPE_GLOVES,
+	EQUIPMENT_TYPE_HELMET,
+	EQUIPMENT_TYPE_SHIELD,
+	EQUIPMENT_TYPE_TOP,
+	EQUIPMENT_TYPE_TROUSERS,
+};
+
+struct equipment {
+	const char *name;
+	const char *description;
+	unsigned int price;
+	enum equipment_type type;
+	struct texture *icon;
+
+	void (*equip)(const struct equipment *, struct character *);
+};
+
+CORE_BEGIN_DECLS
+
+int
+equipment_ok(const struct equipment *);
+
+void
+equipment_equip(const struct equipment *, struct character *);
+
+CORE_END_DECLS
+
+#endif /* !MLK_RPG_EQUIPMENT_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-rpg/mlk/rpg/inventory.c	Sat Oct 15 21:24:17 2022 +0200
@@ -0,0 +1,69 @@
+/*
+ * inventory.c -- item inventory
+ *
+ * Copyright (c) 2020-2022 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 <stddef.h>
+
+#include "inventory.h"
+
+static struct inventory_slot *
+find(struct inventory *iv, const struct item *item)
+{
+	for (size_t i = 0; i < INVENTORY_ITEM_MAX; ++i)
+		if (iv->items[i].item == item)
+			return &iv->items[i];
+
+	return NULL;
+}
+
+int
+inventory_add(struct inventory *iv, const struct item *item, unsigned int amount)
+{
+	assert(iv);
+	assert(item);
+
+	struct inventory_slot *slot;
+
+	/* Find one existing, otherwise find one empty. */
+	if (!(slot = find(iv, item)))
+		slot = find(iv, NULL);
+
+	if (!slot)
+		return 0;
+
+	slot->item = item;
+	slot->amount += amount;
+
+	return -1;
+}
+
+void
+inventory_consume(struct inventory *iv, const struct item *item, unsigned int amount)
+{
+	assert(iv);
+	assert(item);
+
+	struct inventory_slot *slot;
+
+	if ((slot = find(iv, item))) {
+		slot->amount -= amount;
+
+		if (slot->amount == 0)
+			slot->item = NULL;
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-rpg/mlk/rpg/inventory.h	Sat Oct 15 21:24:17 2022 +0200
@@ -0,0 +1,47 @@
+/*
+ * inventory.h -- item inventory
+ *
+ * Copyright (c) 2020-2022 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 MLK_RPG_INVENTORY_H
+#define MLK_RPG_INVENTORY_H
+
+#include <mlk/core/core.h>
+
+#define INVENTORY_ITEM_MAX (512)
+
+struct item;
+
+struct inventory_slot {
+	unsigned int amount;
+	const struct item *item;
+};
+
+struct inventory {
+	struct inventory_slot items[INVENTORY_ITEM_MAX];
+};
+
+CORE_BEGIN_DECLS
+
+int
+inventory_add(struct inventory *, const struct item *, unsigned int);
+
+void
+inventory_consume(struct inventory *, const struct item *, unsigned int);
+
+CORE_END_DECLS
+
+#endif /* !MLK_RPG_INVENTORY_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-rpg/mlk/rpg/item.c	Sat Oct 15 21:24:17 2022 +0200
@@ -0,0 +1,44 @@
+/*
+ * item.c -- inventory items
+ *
+ * Copyright (c) 2020-2022 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 "item.h"
+
+void
+item_exec_menu(const struct item *item, struct character *ch)
+{
+	assert(item);
+	assert(ch);
+
+	item->exec_menu(item, ch);
+}
+
+void
+item_exec_battle(const struct item *item,
+		 struct battle *bt,
+		 struct character *src,
+		 struct character *tgt)
+{
+	assert(item);
+	assert(bt);
+	assert(src);
+	assert(tgt);
+
+	item->exec_battle(item, bt, src, tgt);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-rpg/mlk/rpg/item.h	Sat Oct 15 21:24:17 2022 +0200
@@ -0,0 +1,54 @@
+/*
+ * item.h -- inventory items
+ *
+ * Copyright (c) 2020-2022 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 MLK_RPG_ITEM_H
+#define MLK_RPG_ITEM_H
+
+#include <mlk/core/core.h>
+
+struct battle;
+struct character;
+struct texture;
+
+struct item {
+	const char *name;
+	const char *description;
+	struct texture *icon;
+
+	void (*exec_menu)(const struct item *, struct character *);
+
+	void (*exec_battle)(const struct item *,
+	                    struct battle *,
+	                    struct character *,
+	                    struct character *);
+};
+
+CORE_BEGIN_DECLS
+
+void
+item_exec_menu(const struct item *, struct character *);
+
+void
+item_exec_battle(const struct item *,
+                 struct battle *,
+                 struct character *,
+                 struct character *);
+
+CORE_END_DECLS
+
+#endif /* !MLK_RPG_ITEM_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-rpg/mlk/rpg/map-file.c	Sat Oct 15 21:24:17 2022 +0200
@@ -0,0 +1,312 @@
+/*
+ * map-file.c -- map file loader
+ *
+ * Copyright (c) 2020-2022 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 <errno.h>
+#include <limits.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <mlk/util/util.h>
+
+#include <mlk/core/alloc.h>
+#include <mlk/core/error.h>
+#include <mlk/core/image.h>
+#include <mlk/core/trace.h>
+#include <mlk/core/util.h>
+
+#include "map-file.h"
+
+#define MAX_F(v) MAX_F_(v)
+#define MAX_F_(v) "%" #v "[^\n|]"
+
+struct context {
+	struct map_file *mf;            /* Map loader. */
+	struct map *map;                /* Map object to fill. */
+	FILE *fp;                       /* Map file pointer. */
+	char basedir[PATH_MAX];         /* Parent map directory */
+};
+
+static int
+parse_layer_tiles(struct context *ctx, const char *layer_name)
+{
+	enum map_layer_type layer_type;
+	size_t amount, current;
+
+	if (strcmp(layer_name, "background") == 0)
+		layer_type = MAP_LAYER_TYPE_BACKGROUND;
+	else if (strcmp(layer_name, "foreground") == 0)
+		layer_type = MAP_LAYER_TYPE_FOREGROUND;
+	else if (strcmp(layer_name, "above") == 0)
+		layer_type = MAP_LAYER_TYPE_ABOVE;
+	else
+		return errorf("invalid layer type: %s", layer_name);
+
+	amount = ctx->map->columns * ctx->map->rows;
+	current = 0;
+
+	/*
+	 * The next line after a layer declaration is a list of plain integer
+	 * that fill the layer tiles.
+	 */
+	if (!(ctx->mf->layers[layer_type].tiles = alloc_array0(amount, sizeof (unsigned short))))
+		return -1;
+
+	for (int tile; fscanf(ctx->fp, "%d\n", &tile) && current < amount; ++current)
+		ctx->mf->layers[layer_type].tiles[current] = tile;
+
+	ctx->map->layers[layer_type].tiles = ctx->mf->layers[layer_type].tiles;
+
+	return 0;
+}
+
+static int
+parse_actions(struct context *ctx)
+{
+	char exec[128 + 1];
+	int x = 0, y = 0, block = 0;
+	unsigned int w = 0, h = 0;
+
+	while (fscanf(ctx->fp, "%d|%d|%u|%u|%d|%128[^\n]\n", &x, &y, &w, &h, &block, exec) >= 5) {
+		struct map_block *reg;
+
+		if (!ctx->mf->load_action) {
+			tracef("ignoring action %d,%d,%u,%u,%d,%s", x, y, w, h, block, exec);
+			continue;
+		}
+
+		ctx->mf->load_action(ctx->map, x, y, w, h, exec);
+
+		/*
+		 * Actions do not have concept of collisions because they are
+		 * not only used on maps. The map structure has its very own
+		 * object to manage collisions but the .map file use the same
+		 * directive for simplicity. So create a block region if the
+		 * directive has one.
+		 */
+		if (block) {
+			if (!(reg = alloc_pool_new(&ctx->mf->blocks)))
+				return -1;
+
+			reg->x = x;
+			reg->y = y;
+			reg->w = w;
+			reg->h = h;
+		}
+	}
+
+	/* Reference the blocks array from map_file. */
+	ctx->map->blocks = ctx->mf->blocks.data;
+	ctx->map->blocksz = ctx->mf->blocks.size;
+
+	return 0;
+}
+
+static int
+parse_layer(struct context *ctx, const char *line)
+{
+	char layer_name[32 + 1] = {0};
+
+	/* Check if weight/height has been specified. */
+	if (ctx->map->columns == 0 || ctx->map->rows == 0)
+		return errorf("missing map dimensions before layer");
+
+	/* Determine layer type. */
+	if (sscanf(line, "layer|%32s", layer_name) <= 0)
+		return errorf("missing layer type definition");
+
+	if (strcmp(layer_name, "actions") == 0)
+		return parse_actions(ctx);
+
+	return parse_layer_tiles(ctx, layer_name);
+}
+
+static int
+parse_tileset(struct context *ctx, const char *line)
+{
+	char path[PATH_MAX] = {0}, *p;
+	struct map_file *mf = ctx->mf;
+	struct tileset_file *tf = &mf->tileset_file;
+
+	if (!(p = strchr(line, '|')))
+		return errorf("could not parse tileset");
+
+	snprintf(path, sizeof (path), "%s/%s", ctx->basedir, p + 1);
+
+	if (tileset_file_open(tf, &mf->tileset, path) < 0)
+		return -1;
+
+	ctx->map->tileset = &mf->tileset;
+
+	return 0;
+}
+
+static int
+parse_title(struct context *ctx, const char *line)
+{
+	if (sscanf(line, "title|" MAX_F(MAP_FILE_TITLE_MAX), ctx->mf->title) != 1 || strlen(ctx->mf->title) == 0)
+		return errorf("null map title");
+
+	ctx->map->title = ctx->mf->title;
+
+	return 0;
+}
+
+static int
+parse_columns(struct context *ctx, const char *line)
+{
+	if (sscanf(line, "columns|%u", &ctx->map->columns) != 1 || ctx->map->columns == 0)
+		return errorf("null map columns");
+
+	return 0;
+}
+
+static int
+parse_rows(struct context *ctx, const char *line)
+{
+	if (sscanf(line, "rows|%u", &ctx->map->rows) != 1 || ctx->map->rows == 0)
+		return errorf("null map rows");
+
+	return 0;
+}
+
+static int
+parse_origin(struct context *ctx, const char *line)
+{
+	if (sscanf(line, "origin|%d|%d", &ctx->map->player_x, &ctx->map->player_y) != 2)
+		return errorf("invalid origin");
+
+	return 0;
+}
+
+static int
+parse_line(struct context *ctx, const char *line)
+{
+	static const struct {
+		const char *property;
+		int (*read)(struct context *, const char *);
+	} props[] = {
+		{ "title",      parse_title             },
+		{ "columns",    parse_columns           },
+		{ "rows",       parse_rows              },
+		{ "tileset",    parse_tileset           },
+		{ "origin",     parse_origin            },
+		{ "layer",      parse_layer             },
+	};
+
+	for (size_t i = 0; i < UTIL_SIZE(props); ++i)
+		if (strncmp(line, props[i].property, strlen(props[i].property)) == 0)
+			return props[i].read(ctx, line);
+
+	return 0;
+}
+
+static int
+parse(struct context *ctx, const char *path)
+{
+	char line[1024];
+	char basedir[PATH_MAX];
+
+	util_strlcpy(basedir, path, sizeof (basedir));
+	util_strlcpy(ctx->basedir, util_dirname(basedir), sizeof (ctx->basedir));
+
+	while (fgets(line, sizeof (line), ctx->fp)) {
+		/* Remove \n if any */
+		line[strcspn(line, "\r\n")] = '\0';
+
+		if (parse_line(ctx, line) < 0)
+			return -1;
+	}
+
+	return 0;
+}
+
+static int
+check(struct map *map)
+{
+	/*
+	 * Check that we have parsed every required components.
+	 */
+	if (!map->title)
+		return errorf("missing title");
+
+	/*
+	 * We don't need to check width/height because parsing layers and
+	 * tilesets already check for their presence, so only check layers.
+	 */
+	if (!map->layers[0].tiles)
+		return errorf("missing background layer");
+	if (!map->layers[1].tiles)
+		return errorf("missing foreground layer");
+	if (!tileset_ok(map->tileset))
+		return errorf("missing tileset");
+
+	return 0;
+}
+
+int
+map_file_open(struct map_file *file, struct map *map, const char *path)
+{
+	assert(file);
+	assert(path);
+	assert(map);
+
+	struct context ctx = {
+		.mf = file,
+		.map = map,
+	};
+	int ret = 0;
+
+	memset(map, 0, sizeof (*map));
+	alloc_pool_init(&file->blocks, sizeof (*map->blocks), NULL);
+
+	if (!(ctx.fp = fopen(path, "r")))
+		goto fail;
+	if ((ret = parse(&ctx, path)) < 0 || (ret = check(map)) < 0)
+		goto fail;
+
+	fclose(ctx.fp);
+
+	return 0;
+
+fail:
+	map_finish(map);
+	map_file_finish(file);
+
+	if (ctx.fp)
+		fclose(ctx.fp);
+
+	return -1;
+}
+
+void
+map_file_finish(struct map_file *file)
+{
+	assert(file);
+
+	free(file->layers[0].tiles);
+	free(file->layers[1].tiles);
+	free(file->layers[2].tiles);
+
+	tileset_file_finish(&file->tileset_file);
+	alloc_pool_finish(&file->blocks);
+
+	memset(file, 0, sizeof (*file));
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-rpg/mlk/rpg/map-file.h	Sat Oct 15 21:24:17 2022 +0200
@@ -0,0 +1,53 @@
+/*
+ * map-file.h -- map file loader
+ *
+ * Copyright (c) 2020-2022 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 MLK_RPG_MAP_FILE_H
+#define MLK_RPG_MAP_FILE_H
+
+#include <mlk/core/alloc.h>
+#include <mlk/core/core.h>
+#include <mlk/core/sprite.h>
+#include <mlk/core/texture.h>
+
+#include "map.h"
+#include "tileset.h"
+#include "tileset-file.h"
+
+#define MAP_FILE_TITLE_MAX 64
+
+struct map_file {
+	void (*load_action)(struct map *, int, int, int, int, const char *);
+
+	char title[MAP_FILE_TITLE_MAX];
+	struct map_layer layers[MAP_LAYER_TYPE_NUM];
+	struct tileset_file tileset_file;
+	struct tileset tileset;
+	struct alloc_pool blocks;
+};
+
+CORE_BEGIN_DECLS
+
+int
+map_file_open(struct map_file *file, struct map *map, const char *path);
+
+void
+map_file_finish(struct map_file *file);
+
+CORE_END_DECLS
+
+#endif /* !MLK_RPG_MAP_FILE_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-rpg/mlk/rpg/map.c	Sat Oct 15 21:24:17 2022 +0200
@@ -0,0 +1,672 @@
+/*
+ * map.c -- game map
+ *
+ * Copyright (c) 2020-2022 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 <stdlib.h>
+#include <string.h>
+
+#include <mlk/core/error.h>
+#include <mlk/core/event.h>
+#include <mlk/core/image.h>
+#include <mlk/core/maths.h>
+#include <mlk/core/painter.h>
+#include <mlk/core/sprite.h>
+#include <mlk/core/sys.h>
+#include <mlk/core/texture.h>
+#include <mlk/core/window.h>
+
+#include <mlk/ui/debug.h>
+
+#include "map.h"
+#include "tileset.h"
+
+/*
+ * This is the speed the player moves on the map.
+ *
+ * SPEED represents the number of pixels it must move per SEC.
+ * SEC simply represends the number of milliseconds in one second.
+ */
+#define SPEED 100
+#define SEC   1000
+
+/*
+ * Those are margins within the edge of the screen. The camera always try to
+ * keep those padding between the player and the screen.
+ */
+#define MARGIN_WIDTH    160
+#define MARGIN_HEIGHT   90
+
+#define WIDTH(map)      ((map)->columns * (map)->tileset->sprite->cellw)
+#define HEIGHT(map)     ((map)->rows * (map)->tileset->sprite->cellh)
+
+/*
+ * This structure defines the possible movement of the player as flags since
+ * it's possible to make diagonal movements.
+ */
+enum movement {
+	MOVING_UP       = 1 << 0,
+	MOVING_RIGHT    = 1 << 1,
+	MOVING_DOWN     = 1 << 2,
+	MOVING_LEFT     = 1 << 3
+};
+
+/*
+ * A bit of explanation within this array. The structure walksprite requires
+ * an orientation between 0-7 depending on the user direction.
+ *
+ * Since keys for moving the character may be pressed at the same time, we need
+ * a conversion table from "key pressed" to "orientation".
+ *
+ * When an orientation is impossible, it is set to -1. Example, when both left
+ * and right are pressed.
+ *
+ * MOVING_UP    = 0001 = 0x1
+ * MOVING_RIGHT = 0010 = 0x2
+ * MOVING_DOWN  = 0100 = 0x3
+ * MOVING_LEFT  = 1000 = 0x4
+ */
+static unsigned int orientations[16] = {
+	[0x1] = 0,
+	[0x2] = 2,
+	[0x3] = 1,
+	[0x4] = 4,
+	[0x6] = 3,
+	[0x8] = 6,
+	[0x9] = 7,
+	[0xC] = 5
+};
+
+/*
+ * Check if this block is usable for collision detection. For example if the
+ * player is moving upwards but the collision shape is below it is unnecessary
+ * to check.
+ */
+static int
+is_block_relevant(const struct map *map,
+                    const struct map_block *block,
+                    int drow,
+                    int dcol)
+{
+	if (drow) {
+		/* Object outside of left-right bounds. */
+		if (block->x + (int)block->w <= map->player_x ||
+		    block->x                 >= map->player_x + (int)map->player_sprite->cellw)
+			return 0;
+
+		if ((drow < 0 && block->y            >= map->player_y + (int)map->player_sprite->cellh) ||
+		    (drow > 0 && block->y + block->h <= map->player_y + map->player_sprite->cellh))
+			return 0;
+	} else if (dcol) {
+		/* Object outside of up-down bounds. */
+		if (block->y + (int)block->h <= map->player_y ||
+		    block->y                 >= map->player_y + (int)map->player_sprite->cellh)
+			return 0;
+
+		if ((dcol < 0 && block->x            >= map->player_x + (int)map->player_sprite->cellw) ||
+		    (dcol > 0 && block->x + block->w <= map->player_x + map->player_sprite->cellw))
+			return 0;
+	}
+
+	return 1;
+}
+
+/*
+ * Determine if this collision shape is "closer" to the player by checking the
+ * new block coordinates with the previous one.
+ */
+static int
+is_block_better(const struct map_block *now,
+                const struct map_block *new,
+                int drow,
+                int dcol)
+{
+	return ((drow < 0 && new->y + new->h > now->y + now->h) ||
+	        (drow > 0 && new->y < now->y) ||
+	        (dcol < 0 && new->x + new->w > now->x + now->w) ||
+		(dcol > 0 && new->x < now->x));
+
+}
+
+static void
+center(struct map *map)
+{
+	map->view_x = map->player_x - (int)(map->view_w / 2);
+	map->view_y = map->player_y - (int)(map->view_h / 2);
+
+	if (map->view_x < 0)
+		map->view_x = 0;
+	else if ((unsigned int)map->view_x > WIDTH(map) - map->view_w)
+		map->view_x = WIDTH(map) - map->view_w;
+
+	if (map->view_y < 0)
+		map->view_y = 0;
+	else if ((unsigned int)map->view_y > HEIGHT(map) - map->view_h)
+		map->view_y = HEIGHT(map) - map->view_h;
+}
+
+static void
+init(struct map *map)
+{
+	/* Adjust view. */
+	map->view_w = window.w;
+	map->view_h = window.h;
+
+	/* Adjust margin. */
+	map->margin_w = map->view_w - (MARGIN_WIDTH * 2) - map->player_sprite->cellw;
+	map->margin_h = map->view_h - (MARGIN_HEIGHT * 2) - map->player_sprite->cellh;
+
+	/* Center the view by default. */
+	center(map);
+
+	/* Final bits. */
+	walksprite_init(&map->player_ws, map->player_sprite, 150);
+}
+
+static void
+handle_keydown(struct map *map, const union event *event)
+{
+	switch (event->key.key) {
+	case KEY_UP:
+		map->player_movement |= MOVING_UP;
+		break;
+	case KEY_RIGHT:
+		map->player_movement |= MOVING_RIGHT;
+		break;
+	case KEY_DOWN:
+		map->player_movement |= MOVING_DOWN;
+		break;
+	case KEY_LEFT:
+		map->player_movement |= MOVING_LEFT;
+		break;
+	default:
+		break;
+	}
+
+	map->player_angle = orientations[map->player_movement];
+}
+
+static void
+handle_keyup(struct map *map, const union event *event)
+{
+	switch (event->key.key) {
+	case KEY_TAB:
+		map->flags ^= MAP_FLAGS_SHOW_GRID | MAP_FLAGS_SHOW_COLLIDE;
+		break;
+	case KEY_UP:
+		map->player_movement &= ~(MOVING_UP);
+		break;
+	case KEY_RIGHT:
+		map->player_movement &= ~(MOVING_RIGHT);
+		break;
+	case KEY_DOWN:
+		map->player_movement &= ~(MOVING_DOWN);
+		break;
+	case KEY_LEFT:
+		map->player_movement &= ~(MOVING_LEFT);
+		break;
+	default:
+		break;
+	}
+}
+
+static int
+cmp_tile(const struct tileset_tiledef *td1, const struct tileset_tiledef *td2)
+{
+	if (td1->id < td2->id)
+		return -1;
+	if (td1->id > td2->id)
+		return 1;
+
+	return 0;
+}
+
+static struct tileset_tiledef *
+find_tiledef_by_id(const struct map *map, unsigned short id)
+{
+	typedef int (*cmp)(const void *, const void *);
+
+	const struct tileset_tiledef key = {
+		.id = id
+	};
+
+	return bsearch(&key, map->tileset->tiledefs, map->tileset->tiledefsz,
+	    sizeof (key), (cmp)cmp_tile);
+}
+
+static struct tileset_tiledef *
+find_tiledef_by_row_column_in_layer(const struct map *map,
+                                    const struct map_layer *layer,
+                                    int row,
+                                    int col)
+{
+	unsigned short id;
+
+	if (row < 0 || (unsigned int)row >= map->rows ||
+	    col < 0 || (unsigned int)col >= map->columns)
+		return 0;
+
+	if ((id = layer->tiles[col + row * map->columns]) == 0)
+		return NULL;
+
+	return find_tiledef_by_id(map, id - 1);
+}
+
+static struct tileset_tiledef *
+find_tiledef_by_row_column(const struct map *map, int row, int col)
+{
+	struct tileset_tiledef *tile;
+
+	/* TODO: probably a for loop when we have indefinite layers. */
+	if (!(tile = find_tiledef_by_row_column_in_layer(map, &map->layers[1], row, col)))
+		tile = find_tiledef_by_row_column_in_layer(map, &map->layers[0], row, col);
+
+	return tile;
+}
+
+static void
+find_block_iterate(const struct map *map,
+                   struct map_block *block,
+                   int rowstart,
+                   int rowend,
+                   int colstart,
+                   int colend,
+                   int drow,
+                   int dcol)
+{
+	assert(map);
+	assert(block);
+
+	/* First, check with tiledefs. */
+	for (int r = rowstart; r <= rowend; ++r) {
+		for (int c = colstart; c <= colend; ++c) {
+			struct tileset_tiledef *td;
+			struct map_block tmp;
+
+			if (!(td = find_tiledef_by_row_column(map, r, c)))
+				continue;
+
+			/* Convert to absolute values. */
+			tmp.x = td->x + c * map->tileset->sprite->cellw;
+			tmp.y = td->y + r * map->tileset->sprite->cellh;
+			tmp.w = td->w;
+			tmp.h = td->h;
+
+			/* This tiledef is out of context. */
+			if (!is_block_relevant(map, &tmp, drow, dcol))
+				continue;
+
+			if (is_block_better(block, &tmp, drow, dcol)) {
+				block->x = tmp.x;
+				block->y = tmp.y;
+				block->w = tmp.w;
+				block->h = tmp.h;
+			}
+		}
+	}
+
+	/* Now check if there are objects closer than tiledefs. */
+	for (size_t i = 0; i < map->blocksz; ++i) {
+		const struct map_block *new = &map->blocks[i];
+
+		if (is_block_relevant(map, new, drow, dcol) &&
+		    is_block_better(block, new, drow, dcol)) {
+			block->x = new->x;
+			block->y = new->y;
+			block->w = new->w;
+			block->h = new->h;
+		}
+	}
+}
+
+static void
+find_collision(const struct map *map, struct map_block *block, int drow, int dcolumn)
+{
+	assert((drow && !dcolumn) || (dcolumn && !drow));
+
+	const int playercol = map->player_x / map->tileset->sprite->cellw;
+	const int playerrow = map->player_y / map->tileset->sprite->cellh;
+	const int ncols = (map->player_sprite->cellw / map->tileset->sprite->cellw) + 1;
+	const int nrows = (map->player_sprite->cellh / map->tileset->sprite->cellh) + 1;
+	int rowstart, rowend, colstart, colend;
+
+	if (drow) {
+		colstart = playercol;
+		colend = playercol + ncols;
+
+		if (drow < 0) {
+			/* Moving UP. */
+			rowstart = 0;
+			rowend = playerrow;
+			block->x = block->y = block->h = 0;
+			block->w = WIDTH(map);
+		} else {
+			/* Moving DOWN. */
+			rowstart = playerrow;
+			rowend = HEIGHT(map);
+			block->x = block->h = 0;
+			block->y = HEIGHT(map);
+			block->w = WIDTH(map);
+		}
+	} else {
+		rowstart = playerrow;
+		rowend = playerrow + nrows;
+
+		if (dcolumn < 0) {
+			/* Moving LEFT. */
+			colstart = 0;
+			colend = playercol;
+			block->x = block->y = block->w = 0;
+			block->h = HEIGHT(map);
+		} else {
+			/* Moving RIGHT. */
+			colstart = playercol;
+			colend = WIDTH(map);
+			block->x = WIDTH(map);
+			block->y = block->w = 0;
+			block->h = block->h;
+		}
+	}
+
+	find_block_iterate(map, block, rowstart, rowend, colstart, colend, drow, dcolumn);
+}
+
+static void
+move_x(struct map *map, int delta)
+{
+	struct map_block block;
+
+	find_collision(map, &block, 0, delta < 0 ? -1 : +1);
+
+	if (delta < 0 && map->player_x + delta < (int)(block.x + block.w))
+		delta = map->player_x - block.x - block.w;
+	else if (delta > 0 && (int)(map->player_x + map->player_sprite->cellw + delta) >= block.x)
+		delta = block.x - map->player_x - (int)(map->player_sprite->cellw);
+
+	map->player_x += delta;
+
+	if ((delta < 0 && map->player_x < map->margin_x) ||
+	    (delta > 0 && map->player_x >= (int)(map->margin_x + map->margin_w)))
+		map->view_x += delta;
+
+	if (map->view_x < 0)
+		map->view_x = 0;
+	else if (map->view_x >= (int)(WIDTH(map) - map->view_w))
+		map->view_x = WIDTH(map) - map->view_w;
+}
+
+static void
+move_y(struct map *map, int delta)
+{
+	struct map_block block;
+
+	find_collision(map, &block, delta < 0 ? -1 : +1, 0);
+
+	if (delta < 0 && map->player_y + delta < (int)(block.y + block.h))
+		delta = map->player_y - block.y - block.h;
+	else if (delta > 0 && (int)(map->player_y + map->player_sprite->cellh + delta) >= block.y)
+		delta = block.y - map->player_y - (int)(map->player_sprite->cellh);
+
+	map->player_y += delta;
+
+	if ((delta < 0 && map->player_y < map->margin_y) ||
+	    (delta > 0 && map->player_y >= (int)(map->margin_y + map->margin_h)))
+		map->view_y += delta;
+
+	if (map->view_y < 0)
+		map->view_y = 0;
+	else if (map->view_y >= (int)(HEIGHT(map) - map->view_h))
+		map->view_y = HEIGHT(map) - map->view_h;
+}
+
+static void
+move(struct map *map, unsigned int ticks)
+{
+	/* This is the amount of pixels the player must move. */
+	const int delta = SPEED * ticks / SEC;
+
+	/* This is the rectangle within the view where users must be. */
+	map->margin_x = map->view_x + MARGIN_WIDTH;
+	map->margin_y = map->view_y + MARGIN_HEIGHT;
+
+	int dx = 0;
+	int dy = 0;
+
+	if (map->player_movement == 0)
+		return;
+
+	if (map->player_movement & MOVING_UP)
+		dy = -1;
+	if (map->player_movement & MOVING_DOWN)
+		dy = 1;
+	if (map->player_movement & MOVING_LEFT)
+		dx = -1;
+	if (map->player_movement & MOVING_RIGHT)
+		dx = 1;
+
+	/* Move the player and adjust view if needed. */
+	if (dx)
+		move_x(map, dx * delta);
+	if (dy)
+		move_y(map, dy * delta);
+
+	walksprite_update(&map->player_ws, ticks);
+}
+
+static inline void
+draw_layer_tile(const struct map *map,
+                const struct map_layer *layer,
+                struct texture *colbox,
+                int start_col,
+                int start_row,
+                int start_x,
+                int start_y,
+                unsigned int r,
+                unsigned int c)
+{
+	const struct tileset_tiledef *td;
+	int index, id, sc, sr, mx, my;
+
+	index = (start_col + c) + ((start_row + r) * map->columns);
+
+	if ((id = layer->tiles[index]) == 0)
+		return;
+
+	id -= 1;
+
+	/* Sprite row/column. */
+	sc = (id) % map->tileset->sprite->ncols;
+	sr = (id) / map->tileset->sprite->ncols;
+
+	/* On screen coordinates. */
+	mx = start_x + (int)c * (int)map->tileset->sprite->cellw;
+	my = start_y + (int)r * (int)map->tileset->sprite->cellh;
+
+	tileset_draw(map->tileset, sr, sc, mx, my);
+
+	if ((td = find_tiledef_by_id(map, id)) && texture_ok(colbox))
+		texture_scale(colbox, 0, 0, 5, 5, mx + td->x, my + td->y, td->w, td->h, 0);
+
+	if (map->flags & MAP_FLAGS_SHOW_GRID) {
+		painter_set_color(0x202e37ff);
+		painter_draw_line(mx, my, mx + (int)map->tileset->sprite->cellw, my);
+		painter_draw_line(
+		    mx + (int)map->tileset->sprite->cellw - 1, my,
+		    mx + (int)map->tileset->sprite->cellw - 1, my + (int)map->tileset->sprite->cellh);
+	}
+}
+
+static void
+draw_layer(const struct map *map, const struct map_layer *layer)
+{
+	assert(map);
+	assert(layer);
+
+	/* Beginning of view in row/column. */
+	const unsigned int start_col = map->view_x / map->tileset->sprite->cellw;
+	const unsigned int start_row = map->view_y / map->tileset->sprite->cellh;
+
+	/* Convert into x/y coordinate. */
+	const int start_x = 0 - (map->view_x % (int)map->tileset->sprite->cellw);
+	const int start_y = 0 - (map->view_y % (int)map->tileset->sprite->cellh);
+
+	/* Number of row/columns to draw starting from there. */
+	const unsigned int ncols = (map->view_w / map->tileset->sprite->cellw) + 2;
+	const unsigned int nrows = (map->view_h / map->tileset->sprite->cellh) + 2;
+
+	struct texture colbox = {0};
+
+	if (!layer->tiles)
+		return;
+
+	/* Show collision box if requested. */
+	if (map->flags & MAP_FLAGS_SHOW_COLLIDE && texture_new(&colbox, 16, 16) == 0) {
+		texture_set_blend_mode(&colbox, TEXTURE_BLEND_BLEND);
+		texture_set_alpha_mod(&colbox, 100);
+		PAINTER_BEGIN(&colbox);
+		painter_set_color(0xa53030ff);
+		painter_clear();
+		PAINTER_END();
+	}
+
+	for (unsigned int r = 0; r < nrows; ++r) {
+		for (unsigned int c = 0; c < ncols; ++c) {
+			if (start_col + c >= map->columns ||
+			    start_row + r >= map->rows)
+				continue;
+
+			draw_layer_tile(map, layer, &colbox, start_col, start_row, start_x, start_y, r, c);
+		}
+	}
+
+	texture_finish(&colbox);
+}
+
+static void
+draw_collide(const struct map *map)
+{
+	struct texture box = {0};
+
+	if (map->flags & MAP_FLAGS_SHOW_COLLIDE && texture_new(&box, 64, 64) == 0) {
+		/* Draw collide box around player if requested. */
+		texture_set_alpha_mod(&box, 100);
+		texture_set_blend_mode(&box, TEXTURE_BLEND_BLEND);
+		PAINTER_BEGIN(&box);
+		painter_set_color(0x4f8fbaff);
+		painter_clear();
+		PAINTER_END();
+		texture_scale(&box, 0, 0, 64, 64,
+		    map->player_x - map->view_x, map->player_y - map->view_y,
+			      map->player_sprite->cellw, map->player_sprite->cellh, 0.f);
+
+		/* Do the same for every objects. */
+		PAINTER_BEGIN(&box);
+		painter_set_color(0xa8ca58ff);
+		painter_clear();
+		PAINTER_END();
+
+		for (size_t i = 0; i < map->blocksz; ++i) {
+			texture_scale(&box, 0, 0, 64, 64,
+			    map->blocks[i].x - map->view_x, map->blocks[i].y - map->view_y,
+			    map->blocks[i].w, map->blocks[i].h,
+			    0.f);
+		}
+
+		texture_finish(&box);
+	}
+}
+
+int
+map_init(struct map *map)
+{
+	assert(map);
+
+	init(map);
+	tileset_start(map->tileset);
+
+	return 0;
+}
+
+void
+map_handle(struct map *map, const union event *ev)
+{
+	assert(map);
+	assert(ev);
+
+	switch (ev->type) {
+	case EVENT_KEYDOWN:
+		handle_keydown(map, ev);
+		break;
+	case EVENT_KEYUP:
+		handle_keyup(map, ev);
+		break;
+	default:
+		break;
+	}
+
+	action_stack_handle(&map->astack_par, ev);
+	action_stack_handle(&map->astack_seq, ev);
+}
+
+void
+map_update(struct map *map, unsigned int ticks)
+{
+	assert(map);
+
+	action_stack_update(&map->astack_par, ticks);
+	action_stack_update(&map->astack_seq, ticks);
+
+	tileset_update(map->tileset, ticks);
+
+	/* No movements if the sequential actions are running. */
+	if (action_stack_completed(&map->astack_seq))
+		move(map, ticks);
+}
+
+void
+map_draw(const struct map *map)
+{
+	assert(map);
+
+	/* Draw the texture about background/foreground. */
+	draw_layer(map, &map->layers[MAP_LAYER_TYPE_BACKGROUND]);
+	draw_layer(map, &map->layers[MAP_LAYER_TYPE_FOREGROUND]);
+
+	walksprite_draw(
+		&map->player_ws,
+		map->player_angle,
+		map->player_x - map->view_x,
+		map->player_y - map->view_y);
+
+	draw_layer(map, &map->layers[MAP_LAYER_TYPE_ABOVE]);
+	draw_collide(map);
+
+	action_stack_draw(&map->astack_par);
+	action_stack_draw(&map->astack_seq);
+}
+
+void
+map_finish(struct map *map)
+{
+	assert(map);
+
+	action_stack_finish(&map->astack_par);
+	action_stack_finish(&map->astack_seq);
+
+	memset(map, 0, sizeof (*map));
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-rpg/mlk/rpg/map.h	Sat Oct 15 21:24:17 2022 +0200
@@ -0,0 +1,120 @@
+/*
+ * map.h -- game map
+ *
+ * Copyright (c) 2020-2022 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 MLK_RPG_MAP_H
+#define MLK_RPG_MAP_H
+
+#include <stddef.h>
+
+#include <mlk/core/action.h>
+#include <mlk/core/action-stack.h>
+#include <mlk/core/core.h>
+
+#include "walksprite.h"
+
+struct tileset;
+
+union event;
+
+enum map_layer_type {
+	MAP_LAYER_TYPE_BACKGROUND,
+	MAP_LAYER_TYPE_FOREGROUND,
+	MAP_LAYER_TYPE_ABOVE,
+	MAP_LAYER_TYPE_NUM
+};
+
+struct map_layer {
+	unsigned short *tiles;
+};
+
+enum map_flags {
+	MAP_FLAGS_NONE          = 0,
+	MAP_FLAGS_SHOW_GRID     = (1 << 0),
+	MAP_FLAGS_SHOW_COLLIDE  = (1 << 2)
+};
+
+struct map_block {
+	int x;
+	int y;
+	unsigned int w;
+	unsigned int h;
+};
+
+struct map {
+	const char *title;              /*!< (+) Map title name. */
+	unsigned int columns;           /*!< (-) Number of columns. */
+	unsigned int rows;              /*!< (-) Number of rows. */
+
+	/* Tileset. */
+	struct tileset *tileset;        /*!< (+&?) Tileset to use. */
+
+	/* View options. */
+	enum map_flags flags;           /*!< (+) View options. */
+
+	/* Extra collisions blocks. */
+	struct map_block *blocks;       /*!< (+&?) Extra collisions. */
+	size_t blocksz;                 /*!< (+) Number of collisions. */
+
+	/* List of actions. */
+	struct action_stack astack_par; /*!< (+) Parallel actions. */
+	struct action_stack astack_seq; /*!< (+) Blocking actions. */
+
+	/* Player. */
+	struct sprite *player_sprite;   /*!< (+) The sprite to use */
+	struct walksprite player_ws;    /*!< (-) Walking sprite for moving the player. */
+	int player_x;                   /*!< (+) Player position in x */
+	int player_y;                   /*!< (+) Player position in y */
+	int player_angle;               /*!< (+) Player angle (see walksprite) */
+	unsigned int player_movement;   /*!< (*) Current player movements. */
+
+	/* View to zoom/locate. */
+	int view_x;                     /*!< (+) Position in x */
+	int view_y;                     /*!< (+) Position in y */
+	unsigned int view_w;            /*!< (+) View width */
+	unsigned int view_h;            /*!< (+) View height */
+
+	/* View margin. */
+	int margin_x;                   /*!< (+) View margin in x. */
+	int margin_y;                   /*!< (+) View margin in y. */
+	unsigned int margin_w;          /*!< (+) Margin width. */
+	unsigned int margin_h;          /*!< (+) Margin height. */
+
+	/* Different tile layers. */
+	struct map_layer layers[MAP_LAYER_TYPE_NUM];
+};
+
+CORE_BEGIN_DECLS
+
+int
+map_init(struct map *map);
+
+void
+map_handle(struct map *map, const union event *ev);
+
+void
+map_update(struct map *map, unsigned int ticks);
+
+void
+map_draw(const struct map *map);
+
+void
+map_finish(struct map *map);
+
+CORE_END_DECLS
+
+#endif /* !MLK_RPG_MAP_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-rpg/mlk/rpg/message.c	Sat Oct 15 21:24:17 2022 +0200
@@ -0,0 +1,316 @@
+/*
+ * message.c -- message dialog
+ *
+ * Copyright (c) 2020-2022 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 <stdlib.h>
+#include <string.h>
+
+#include <mlk/core/action.h>
+#include <mlk/core/event.h>
+#include <mlk/core/font.h>
+#include <mlk/core/maths.h>
+#include <mlk/core/painter.h>
+#include <mlk/core/panic.h>
+#include <mlk/core/sprite.h>
+#include <mlk/core/trace.h>
+#include <mlk/core/util.h>
+
+#include <mlk/ui/align.h>
+#include <mlk/ui/frame.h>
+#include <mlk/ui/label.h>
+#include <mlk/ui/theme.h>
+
+#include "message.h"
+
+#define THEME(msg)      (msg->theme ? msg->theme : theme_default())
+
+static void
+handle(struct action *action, const union event *ev)
+{
+	assert(action);
+	assert(ev);
+
+	message_handle(action->data, ev);
+}
+
+static int
+update(struct action *action, unsigned int ticks)
+{
+	assert(action);
+
+	return message_update(action->data, ticks);
+}
+
+static void
+draw(struct action *action)
+{
+	assert(action);
+
+	message_draw(action->data);
+}
+
+static void
+draw_frame(const struct message *msg)
+{
+	assert(msg);
+
+	struct frame frame = {
+		.w = msg->w,
+		.h = msg->h,
+		.theme = msg->theme
+	};
+
+	frame_draw(&frame);
+}
+
+static inline unsigned int
+min_width(const struct message *msg)
+{
+	assert(msg);
+
+	unsigned int maxw = 0, w = 0;
+
+	for (size_t i = 0; i < msg->linesz; ++i) {
+		if (!msg->lines[i])
+			continue;
+		if (font_query(THEME(msg)->fonts[THEME_FONT_INTERFACE], msg->lines[i], &w, NULL) < 0)
+			panic();
+		if (w > maxw)
+			maxw = w;
+	}
+
+	return (THEME(msg)->padding * 2) + maxw;
+}
+
+static inline unsigned int
+min_height(const struct message *msg)
+{
+	assert(msg);
+
+	const struct theme *th = THEME(msg);
+	const unsigned int lh  = font_height(th->fonts[THEME_FONT_INTERFACE]);
+
+	return (th->padding * 2) + (msg->linesz * lh) + ((msg->linesz - 1) * msg->spacing);
+}
+
+static void
+draw_lines(const struct message *msg)
+{
+	const struct theme *theme = THEME(msg);
+	struct label label;
+	unsigned int lw, lh;
+
+	for (size_t i = 0; i < msg->linesz; ++i) {
+		if (!msg->lines[i])
+			continue;
+		if (font_query(theme->fonts[THEME_FONT_INTERFACE], msg->lines[i], &lw, &lh) < 0)
+			panic();
+
+		label.theme = theme;
+		label.x = theme->padding;
+		label.y = theme->padding + (i * (lh + msg->spacing));
+		label.text = msg->lines[i];
+		label.flags = LABEL_FLAGS_SHADOW;
+
+		if (label.x + lw > msg->w)
+			tracef("message width too small: %u < %u", msg->w, min_width(msg));
+		if (label.y + lh > msg->h)
+			tracef("message height too small: %u < %u", msg->h, min_height(msg));
+
+		/*
+		 * The function label_draw will use THEME_COLOR_NORMAL to draw
+		 * text and THEME_COLOR_SHADOW so if we have selected a line
+		 * we need to cheat the normal color.
+		 */
+		if (msg->flags & MESSAGE_FLAGS_QUESTION && msg->index == (unsigned int)i)
+			label.flags |= LABEL_FLAGS_SELECTED;
+		else
+			label.flags &= ~(LABEL_FLAGS_SELECTED);
+
+		label_draw(&label);
+	}
+}
+
+void
+message_start(struct message *msg)
+{
+	assert(msg);
+
+	if (msg->flags & (MESSAGE_FLAGS_FADEIN|MESSAGE_FLAGS_FADEOUT))
+		assert(msg->delay > 0);
+
+	msg->elapsed = 0;
+	msg->scale = msg->flags & MESSAGE_FLAGS_FADEIN ? 0.0 : 1.0;
+	msg->state = msg->flags & MESSAGE_FLAGS_FADEIN
+	    ? MESSAGE_STATE_OPENING
+	    : MESSAGE_STATE_SHOWING;
+
+	if (msg->flags & MESSAGE_FLAGS_AUTOMATIC && msg->timeout == 0)
+		tracef("message is automatic but has zero timeout");
+}
+
+void
+message_query(const struct message *msg, unsigned int *w, unsigned int *h)
+{
+	assert(msg);
+
+	if (w)
+		*w = min_width(msg);
+	if (h)
+		*h = min_height(msg);
+}
+
+void
+message_handle(struct message *msg, const union event *ev)
+{
+	assert(msg);
+	assert(ev);
+
+	/* Skip if the message animation hasn't complete. */
+	if (msg->state != MESSAGE_STATE_SHOWING)
+		return;
+
+	/* Only keyboard event are valid. */
+	if (ev->type != EVENT_KEYDOWN || msg->state == MESSAGE_STATE_NONE)
+		return;
+
+	switch (ev->key.key) {
+	case KEY_UP:
+		if (msg->index > 0)
+			msg->index--;
+		break;
+	case KEY_DOWN:
+		if (msg->index + 1 < msg->linesz && msg->lines[msg->index + 1])
+			msg->index++;
+		break;
+	case KEY_ENTER:
+		msg->state = msg->flags & MESSAGE_FLAGS_FADEOUT
+		    ? MESSAGE_STATE_HIDING
+		    : MESSAGE_STATE_NONE;
+		msg->elapsed = 0;
+		break;
+	default:
+		break;
+	}
+}
+
+int
+message_update(struct message *msg, unsigned int ticks)
+{
+	assert(msg);
+
+	msg->elapsed += ticks;
+
+	switch (msg->state) {
+	case MESSAGE_STATE_OPENING:
+		msg->scale = (double)msg->elapsed / (double)msg->delay;
+
+		if (msg->scale > 1)
+			msg->scale = 1;
+
+		if (msg->elapsed >= msg->delay) {
+			msg->state = MESSAGE_STATE_SHOWING;
+			msg->elapsed = 0;
+		}
+
+		break;
+	case MESSAGE_STATE_SHOWING:
+		/* Do automatically switch state if requested by the user. */
+		if (msg->flags & MESSAGE_FLAGS_AUTOMATIC && msg->elapsed >= msg->timeout) {
+			msg->state = msg->flags & MESSAGE_FLAGS_FADEOUT
+			    ? MESSAGE_STATE_HIDING
+			    : MESSAGE_STATE_NONE;
+			msg->elapsed = 0;
+		}
+
+		break;
+	case MESSAGE_STATE_HIDING:
+		msg->scale = 1 - (double)msg->elapsed / (double)msg->delay;
+
+		if (msg->scale < 0)
+			msg->scale = 0;
+		if (msg->elapsed >= msg->delay) {
+			msg->state = MESSAGE_STATE_NONE;
+			msg->elapsed = 0;
+		}
+
+		break;
+	default:
+		break;
+	}
+
+	return msg->state == MESSAGE_STATE_NONE;
+}
+
+void
+message_draw(const struct message *msg)
+{
+	assert(msg);
+
+	struct texture tex;
+	int x, y;
+	unsigned int w, h;
+
+	if (msg->w == 0 || msg->h == 0) {
+		tracef("message has null dimensions");
+		return;
+	}
+
+	if (texture_new(&tex, msg->w, msg->h) < 0)
+		panic();
+
+	PAINTER_BEGIN(&tex);
+	draw_frame(msg);
+	draw_lines(msg);
+	PAINTER_END();
+
+	/* Compute scaling. */
+	w = msg->w * msg->scale;
+	h = msg->h * msg->scale;
+
+	/* Centerize within its drawing area. */
+	align(ALIGN_CENTER, &x, &y, w, h, msg->x, msg->y, msg->w, msg->h);
+
+	/* Draw and clear. */
+	texture_scale(&tex, 0, 0, msg->w, msg->h, x, y, w, h, 0);
+	texture_finish(&tex);
+}
+
+void
+message_hide(struct message *msg)
+{
+	assert(msg);
+
+	msg->state = MESSAGE_STATE_HIDING;
+	msg->elapsed = 0;
+}
+
+void
+message_action(struct message *msg, struct action *action)
+{
+	assert(msg);
+	assert(action);
+
+	memset(action, 0, sizeof (struct action));
+	action->data = msg;
+	action->handle = handle;
+	action->update = update;
+	action->draw = draw;
+
+	message_start(msg);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-rpg/mlk/rpg/message.h	Sat Oct 15 21:24:17 2022 +0200
@@ -0,0 +1,91 @@
+/*
+ * message.h -- message dialog
+ *
+ * Copyright (c) 2020-2022 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 MLK_RPG_MESSAGE_H
+#define MLK_RPG_MESSAGE_H
+
+#include <mlk/core/core.h>
+#include <mlk/core/texture.h>
+
+struct action;
+struct font;
+struct theme;
+
+union event;
+
+#define MESSAGE_DELAY_DEFAULT           (150)
+#define MESSAGE_TIMEOUT_DEFAULT         (5000)
+
+enum message_flags {
+	MESSAGE_FLAGS_AUTOMATIC         = (1 << 0),
+	MESSAGE_FLAGS_QUESTION          = (1 << 1),
+	MESSAGE_FLAGS_FADEIN            = (1 << 2),
+	MESSAGE_FLAGS_FADEOUT           = (1 << 3)
+};
+
+enum message_state {
+	MESSAGE_STATE_NONE,
+	MESSAGE_STATE_OPENING,
+	MESSAGE_STATE_SHOWING,
+	MESSAGE_STATE_HIDING
+};
+
+struct message {
+	int x;
+	int y;
+	unsigned int w;
+	unsigned int h;
+	unsigned int spacing;
+	unsigned int delay;
+	unsigned int timeout;
+	const char * const *lines;
+	size_t linesz;
+	unsigned int index;
+	enum message_flags flags;
+	enum message_state state;
+	const struct theme *theme;
+	unsigned int elapsed;
+	double scale;
+};
+
+CORE_BEGIN_DECLS
+
+void
+message_start(struct message *msg);
+
+void
+message_query(const struct message *msg, unsigned int *w, unsigned int *h);
+
+void
+message_handle(struct message *msg, const union event *ev);
+
+int
+message_update(struct message *msg, unsigned int ticks);
+
+void
+message_draw(const struct message *msg);
+
+void
+message_hide(struct message *msg);
+
+void
+message_action(struct message *msg, struct action *act);
+
+CORE_END_DECLS
+
+#endif /* !MLK_RPG_MESSAGE_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-rpg/mlk/rpg/property.c	Sat Oct 15 21:24:17 2022 +0200
@@ -0,0 +1,63 @@
+/*
+ * property.c -- manage game properties
+ *
+ * Copyright (c) 2020-2022 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/property-save.h>
+#include <assets/sql/property-remove.h>
+#include <assets/sql/property-load.h>
+
+#include "property.h"
+#include "save.h"
+
+int
+property_save(const struct property *p, struct save *s)
+{
+	assert(p);
+	assert(save_ok(s));
+
+	return save_exec(s, (const char *)assets_property_save, "ss", p->key, p->value);
+}
+
+int
+property_load(struct property *p, struct save *s)
+{
+	assert(p);
+	assert(save_ok(s));
+
+	struct save_stmt stmt;
+	enum save_stmt_errno ret;
+
+	if (save_stmt_init(&stmt, s, (const char *)assets_property_load, "s", p->key) < 0)
+		return -1;
+
+	ret = save_stmt_next(&stmt, "s", p->value, sizeof (p->value)) == SAVE_STMT_ROW;
+	save_stmt_finish(&stmt);
+
+	return ret ? 0 : -1;
+}
+
+int
+property_remove(struct property *p, struct save *s)
+{
+	assert(p);
+	assert(save_ok(s));
+
+	return save_exec(s, (const char *)assets_property_remove, "s", p->key);
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-rpg/mlk/rpg/property.h	Sat Oct 15 21:24:17 2022 +0200
@@ -0,0 +1,47 @@
+/*
+ * property.h -- manage game properties
+ *
+ * Copyright (c) 2020-2022 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 MLK_RPG_PROPERTY_H
+#define MLK_RPG_PROPERTY_H
+
+#include <mlk/core/core.h>
+
+#define PROPERTY_KEY_MAX        (64)
+#define PROPERTY_VALUE_MAX      (1024)
+
+struct save;
+
+struct property {
+	char key[PROPERTY_KEY_MAX + 1];
+	char value[PROPERTY_VALUE_MAX + 1];
+};
+
+CORE_BEGIN_DECLS
+
+int
+property_save(const struct property *, struct save *);
+
+int
+property_load(struct property *, struct save *);
+
+int
+property_remove(struct property *, struct save *);
+
+CORE_END_DECLS
+
+#endif /* !MLK_RPG_PROPERTY_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-rpg/mlk/rpg/quest.c	Sat Oct 15 21:24:17 2022 +0200
@@ -0,0 +1,83 @@
+/*
+ * quest.c -- in game quests
+ *
+ * Copyright (c) 2020-2022 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_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_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_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/libmlk-rpg/mlk/rpg/quest.h	Sat Oct 15 21:24:17 2022 +0200
@@ -0,0 +1,51 @@
+/*
+ * quest.h -- in game quests
+ *
+ * Copyright (c) 2020-2022 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 MLK_RPG_QUEST_H
+#define MLK_RPG_QUEST_H
+
+#include <stddef.h>
+
+#include <mlk/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 /* !MLK_RPG_QUEST_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-rpg/mlk/rpg/rpg.c	Sat Oct 15 21:24:17 2022 +0200
@@ -0,0 +1,30 @@
+/*
+ * rpg.c -- librpg convenient header
+ *
+ * Copyright (c) 2020-2022 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 "rpg.h"
+
+int
+rpg_init(void)
+{
+	return 0;
+}
+
+void
+rpg_finish(void)
+{
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-rpg/mlk/rpg/rpg.h	Sat Oct 15 21:24:17 2022 +0200
@@ -0,0 +1,34 @@
+/*
+ * rpg.h -- librpg convenient header
+ *
+ * Copyright (c) 2020-2022 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 MLK_RPG_RPG_H
+#define MLK_RPG_RPG_H
+
+#include <mlk/core/core.h>
+
+CORE_BEGIN_DECLS
+
+int
+rpg_init(void);
+
+void
+rpg_finish(void);
+
+CORE_END_DECLS
+
+#endif /* !MLK_RPG_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-rpg/mlk/rpg/save.c	Sat Oct 15 21:24:17 2022 +0200
@@ -0,0 +1,318 @@
+/*
+ * save.c -- save functions
+ *
+ * Copyright (c) 2020-2022 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 <stdlib.h>
+#include <string.h>
+
+#include <sqlite3.h>
+
+#include <mlk/util/util.h>
+
+#include <mlk/core/error.h>
+#include <mlk/core/sys.h>
+#include <mlk/core/util.h>
+
+#include <assets/sql/init.h>
+
+#include "property.h"
+#include "save.h"
+
+#define SQL_BEGIN       "BEGIN EXCLUSIVE TRANSACTION"
+#define SQL_COMMIT      "COMMIT"
+#define SQL_ROLLBACK    "ROLLBACK"
+
+static int
+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 0;
+}
+
+static const char *
+path(unsigned int idx)
+{
+	return util_pathf("%s%u.db", sys_dir(SYS_DIR_SAVE), idx);
+}
+
+static int
+execu(struct save *db, const unsigned char *sql)
+{
+	return exec(db, (const char *)sql);
+}
+
+static int
+verify(struct save *db)
+{
+	struct {
+		time_t *date;
+		struct 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 (property_load(&table[i].prop, db) < 0) {
+			sqlite3_close(db->handle);
+			return errorf("database not initialized correctly");
+		}
+
+		*table[i].date = strtoull(table[i].prop.value, NULL, 10);
+	}
+
+	return 0;
+}
+
+static int
+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 -1;
+			break;
+		case 's':
+			if (sqlite3_bind_text(stmt->handle, i++, va_arg(ap, const char *), -1, NULL) != SQLITE_OK)
+				return -1;
+			break;
+		case 't':
+			if (sqlite3_bind_int64(stmt->handle, i++, va_arg(ap, time_t)) != SQLITE_OK)
+				return -1;
+			break;
+		case ' ':
+			break;
+		default:
+			return errorf("invalid format: %c", *args);
+		}
+	}
+
+	return 0;
+
+sqlite3_err:
+	return errorf("%s", sqlite3_errmsg(s->handle));
+}
+
+static int
+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);
+
+			util_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 0;
+}
+
+int
+save_open(struct save *db, unsigned int idx, enum save_mode mode)
+{
+	assert(db);
+
+	return save_open_path(db, path(idx), mode);
+}
+
+int
+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, assets_init) < 0)
+		goto sqlite3_err;
+
+	return verify(db);
+
+sqlite3_err:
+	errorf("%s", sqlite3_errmsg(db->handle));
+	sqlite3_close(db->handle);
+
+	memset(db, 0, sizeof (*db));
+
+	return -1;
+}
+
+int
+save_ok(const struct save *db)
+{
+	assert(db);
+
+	return db && db->handle;
+}
+
+int
+save_exec(struct save *db, const char *sql, const char *args, ...)
+{
+	assert(save_ok(db));
+	assert(sql);
+
+	struct save_stmt stmt;
+	enum save_stmt_errno ret;
+	va_list ap;
+
+	va_start(ap, args);
+	ret = prepare(db, &stmt, sql, args, ap);
+	va_end(ap);
+
+	if (ret < 0)
+		return -1;
+
+	ret = save_stmt_next(&stmt, NULL);
+	save_stmt_finish(&stmt);
+
+	return ret == SAVE_STMT_ERROR ? -1 : 0;
+}
+
+void
+save_finish(struct save *db)
+{
+	assert(db);
+
+	if (db->handle)
+		sqlite3_close(db->handle);
+
+	memset(db, 0, sizeof (*db));
+}
+
+int
+save_stmt_init(struct save_stmt *stmt, struct save *db, const char *sql, const char *args, ...)
+{
+	assert(stmt);
+	assert(save_ok(db));
+	assert(args);
+
+	va_list ap;
+	int ret;
+
+	va_start(ap, args);
+	ret = prepare(db, stmt, sql, args, ap);
+	va_end(ap);
+
+	return ret;
+}
+
+enum save_stmt_errno
+save_stmt_next(struct save_stmt *stmt, const char *args, ...)
+{
+	assert(stmt);
+
+	va_list ap;
+	enum save_stmt_errno ret = SAVE_STMT_ERROR;
+
+	switch (sqlite3_step(stmt->handle)) {
+	case SQLITE_ROW:
+		va_start(ap, args);
+
+		if (extract(stmt, args, ap) == 0)
+			ret = SAVE_STMT_ROW;
+
+		va_end(ap);
+		break;
+	case SQLITE_DONE:
+		ret = SAVE_STMT_DONE;
+		break;
+	default:
+		errorf("%s", sqlite3_errmsg(stmt->parent->handle));
+		break;
+	}
+
+	return ret;
+}
+
+void
+save_stmt_finish(struct save_stmt *stmt)
+{
+	assert(stmt);
+
+	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);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-rpg/mlk/rpg/save.h	Sat Oct 15 21:24:17 2022 +0200
@@ -0,0 +1,87 @@
+/*
+ * save.h -- save functions
+ *
+ * Copyright (c) 2020-2022 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 MLK_RPG_SAVE_H
+#define MLK_RPG_SAVE_H
+
+#include <time.h>
+
+#include <mlk/core/core.h>
+
+struct save {
+	time_t created;
+	time_t updated;
+	void *handle;
+};
+
+enum save_mode {
+	SAVE_MODE_READ,
+	SAVE_MODE_WRITE
+};
+
+struct save_stmt {
+	struct save *parent;
+	void *handle;
+};
+
+enum save_stmt_errno {
+	SAVE_STMT_DONE,
+	SAVE_STMT_ROW,
+	SAVE_STMT_ERROR
+};
+
+CORE_BEGIN_DECLS
+
+int
+save_open(struct save *, unsigned int, enum save_mode);
+
+int
+save_open_path(struct save *, const char *, enum save_mode);
+
+int
+save_ok(const struct save *);
+
+int
+save_exec(struct save *, const char *, const char *, ...);
+
+void
+save_finish(struct save *);
+
+/* Prepared statements. */
+int
+save_stmt_init(struct save_stmt *, struct save *, const char *, const char *, ...);
+
+enum save_stmt_errno
+save_stmt_next(struct save_stmt *, const char *, ...);
+
+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 /* !MLK_RPG_SAVE_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-rpg/mlk/rpg/selection.c	Sat Oct 15 21:24:17 2022 +0200
@@ -0,0 +1,68 @@
+/*
+ * selection.c -- kind of selection
+ *
+ * Copyright (c) 2020-2022 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 <mlk/core/util.h>
+
+#include "battle.h"
+#include "character.h"
+#include "selection.h"
+
+static void
+random(struct selection *slt, struct battle_entity **entities, size_t entitiesz)
+{
+	do {
+		slt->index_character = util_nrand(0, entitiesz);
+	} while (!battle_entity_ok(entities[slt->index_character]));
+}
+
+static void
+first(struct selection *slt, struct battle_entity **entities, size_t entitiesz)
+{
+	for (size_t i = 0; i < entitiesz; ++i) {
+		if (battle_entity_ok(entities[i])) {
+			slt->index_character = i;
+			break;
+		}
+	}
+}
+
+void
+selection_first(struct selection *slt, const struct battle *bt)
+{
+	assert(slt);
+	assert(bt);
+
+	if (slt->index_side == 0)
+		first(slt, bt->enemies, bt->enemiesz);
+	else
+		first(slt, bt->team, bt->teamsz);
+}
+
+void
+selection_random(struct selection *slt, const struct battle *bt)
+{
+	assert(slt);
+	assert(bt);
+
+	if (slt->index_side == 0)
+		random(slt, bt->enemies, bt->enemiesz);
+	else
+		random(slt, bt->team, bt->teamsz);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-rpg/mlk/rpg/selection.h	Sat Oct 15 21:24:17 2022 +0200
@@ -0,0 +1,60 @@
+/*
+ * selection.h -- kind of selection
+ *
+ * Copyright (c) 2020-2022 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 MLK_RPG_SELECTION_H
+#define MLK_RPG_SELECTION_H
+
+#include <mlk/core/core.h>
+
+struct battle;
+
+enum selection_kind {
+	SELECTION_KIND_SELF,
+	SELECTION_KIND_ONE,
+	SELECTION_KIND_ALL,
+	SELECTION_KIND_BOTH
+};
+
+enum selection_side {
+	/* Which side allowed (can be both). */
+	SELECTION_SIDE_TEAM     = (1 << 0),
+	SELECTION_SIDE_ENEMY    = (1 << 1)
+};
+
+struct selection {
+	enum selection_kind allowed_kinds;
+	enum selection_side allowed_sides;
+
+	/* Character index in battle entity array. */
+	unsigned int index_character;
+
+	/* Side index (0 = enemy, 1 = team). */
+	unsigned int index_side;
+};
+
+CORE_BEGIN_DECLS
+
+void
+selection_first(struct selection *, const struct battle *);
+
+void
+selection_random(struct selection *, const struct battle *);
+
+CORE_END_DECLS
+
+#endif /* !MLK_RPG_SELECTION_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-rpg/mlk/rpg/spell.c	Sat Oct 15 21:24:17 2022 +0200
@@ -0,0 +1,52 @@
+/*
+ * spell.c -- magic spells
+ *
+ * Copyright (c) 2020-2022 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 "spell.h"
+
+void
+spell_select(const struct spell *s, const struct battle *bt, struct selection *slt)
+{
+	assert(s && s->select);
+	assert(bt);
+	assert(slt);
+
+	s->select(bt, slt);
+}
+
+void
+spell_action(const struct spell *s, struct battle *bt, struct character *owner, const struct selection *slt)
+{
+	assert(s && s->action);
+	assert(bt);
+	assert(owner);
+	assert(slt);
+
+	s->action(bt, owner, slt);
+}
+
+void
+spell_use(struct spell *s, struct character *owner, const struct selection *slt)
+{
+	assert(s && s->use);
+	assert(owner);
+	assert(slt);
+
+	s->use(owner, slt);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-rpg/mlk/rpg/spell.h	Sat Oct 15 21:24:17 2022 +0200
@@ -0,0 +1,67 @@
+/*
+ * spell.h -- magic spells
+ *
+ * Copyright (c) 2020-2022 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 MLK_RPG_SPELL_H
+#define MLK_RPG_SPELL_H
+
+#include <mlk/core/core.h>
+
+#include "selection.h"
+
+struct character;
+struct battle;
+struct selection;
+
+enum spell_type {
+	SPELL_TYPE_NEUTRAL,
+	SPELL_TYPE_FIRE,
+	SPELL_TYPE_WIND,
+	SPELL_TYPE_WATER,
+	SPELL_TYPE_EARTH,
+	SPELL_TYPE_CHAOS,
+	SPELL_TYPE_HOLY,
+	SPELL_TYPE_TIME
+};
+
+struct spell {
+	const char *name;
+	const char *description;
+	unsigned int mp;
+	enum spell_type type;
+	enum selection_kind select_kind;
+	enum selection_side select_side;
+
+	void (*select)(const struct battle *, struct selection *);
+	void (*action)(struct battle *, struct character *, const struct selection *);
+	void (*use)(struct character *, const struct selection *);
+};
+
+CORE_BEGIN_DECLS
+
+void
+spell_select(const struct spell *, const struct battle *, struct selection *);
+
+void
+spell_action(const struct spell *, struct battle *, struct character *, const struct selection *);
+
+void
+spell_use(struct spell *, struct character *, const struct selection *);
+
+CORE_END_DECLS
+
+#endif /* !MLK_RPG_SPELL_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-rpg/mlk/rpg/team.c	Sat Oct 15 21:24:17 2022 +0200
@@ -0,0 +1,20 @@
+/*
+ * team.c -- team storage
+ *
+ * Copyright (c) 2020-2022 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. */
+enum { some_compiler_needs_more_than_nothing = 1 };
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-rpg/mlk/rpg/team.h	Sat Oct 15 21:24:17 2022 +0200
@@ -0,0 +1,30 @@
+/*
+ * team.h -- team storage
+ *
+ * Copyright (c) 2020-2022 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 MLK_RPG_TEAM_H
+#define MLK_RPG_TEAM_H
+
+#define TEAM_MEMBER_MAX (4)
+
+struct character;
+
+struct team {
+	struct character *members[TEAM_MEMBER_MAX];
+};
+
+#endif /* MLK_RPG_TEAM_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-rpg/mlk/rpg/tileset-file.c	Sat Oct 15 21:24:17 2022 +0200
@@ -0,0 +1,334 @@
+/*
+ * tileset-file.c -- tileset file loader
+ *
+ * Copyright (c) 2020-2022 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 <errno.h>
+#include <limits.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <mlk/util/util.h>
+
+#include <mlk/core/alloc.h>
+#include <mlk/core/animation.h>
+#include <mlk/core/error.h>
+#include <mlk/core/image.h>
+#include <mlk/core/util.h>
+
+#include "tileset-file.h"
+#include "tileset.h"
+
+#define MAX_F(v) MAX_F_(v)
+#define MAX_F_(v) "%" #v "[^\n|]"
+
+/*
+ * This is how memory for animations is allocated in the tileset_file
+ * structure.
+ *
+ * As animations require a texture and a sprite to be present, we need to store
+ * them locally in the tileset_file structure.
+ *
+ * tileset_file->anims[0] array (struct tileset_animation_block):
+ *
+ * [0]            [1]            [N]
+ *  | texture      | texture      | texture
+ *  | sprite       | sprite       | sprite
+ *  | animation    | animation    | animation
+ *
+ * tileset_file->anims[1] array (struct tileset_animation):
+ *
+ * [0]            [1]            [N]
+ *  | id           | id           | id
+ *  | animation ^  | animation ^  | animation ^
+ *
+ * The second array is the exposed array through the tileset->anims pointer,
+ * animations are referenced from the first array. This is because user may need
+ * or replace the tileset by itself and as such we need to keep track of the
+ * resource the tileset_file has allocated itself.
+ */
+
+struct tileset_animation_block {
+	struct texture texture;
+	struct sprite sprite;
+	struct animation animation;
+};
+
+struct context {
+	struct tileset_file *tf;
+	struct tileset *tileset;
+	FILE *fp;
+
+	char basedir[PATH_MAX];
+
+	/*
+	 * The following properties aren't stored in the tileset because they
+	 * are not needed after loading.
+	 */
+	unsigned int tilewidth;
+	unsigned int tileheight;
+
+	/* Number of rows/columns in the image. */
+	unsigned int nrows;
+	unsigned int ncolumns;
+};
+
+static void
+tileset_animation_block_finish(void *data)
+{
+	struct tileset_animation_block *anim = data;
+
+	texture_finish(&anim->texture);
+}
+
+static int
+tileset_tiledef_cmp(const void *d1, const void *d2)
+{
+	const struct tileset_tiledef *mtd1 = d1;
+	const struct tileset_tiledef *mtd2 = d2;
+
+	if (mtd1->id < mtd2->id)
+		return -1;
+	if (mtd1->id > mtd2->id)
+		return 1;
+
+	return 0;
+}
+
+static int
+tileset_animation_cmp(const void *d1, const void *d2)
+{
+	const struct tileset_animation *mtd1 = d1;
+	const struct tileset_animation *mtd2 = d2;
+
+	if (mtd1->id < mtd2->id)
+		return -1;
+	if (mtd1->id > mtd2->id)
+		return 1;
+
+	return 0;
+}
+
+static int
+parse_tilewidth(struct context *ctx, const char *line)
+{
+	if (sscanf(line, "tilewidth|%u", &ctx->tilewidth) != 1 || ctx->tilewidth == 0)
+		return errorf("tilewidth is null");
+
+	return 0;
+}
+
+static int
+parse_tileheight(struct context *ctx, const char *line)
+{
+	if (sscanf(line, "tileheight|%u", &ctx->tileheight) != 1 || ctx->tileheight == 0)
+		return errorf("tileheight is null");
+
+	return 0;
+}
+
+static int
+parse_tiledefs(struct context *ctx, const char *line)
+{
+	(void)line;
+
+	short x, y;
+	unsigned short id, w, h;
+	struct tileset_tiledef *td;
+
+	alloc_pool_init(&ctx->tf->tiledefs, sizeof (*td), NULL);
+
+	while (fscanf(ctx->fp, "%hu|%hd|%hd|%hu|%hu\n", &id, &x, &y, &w, &h) == 5) {
+		td = alloc_pool_new(&ctx->tf->tiledefs);
+		td->id = id;
+		td->x = x;
+		td->y = y;
+		td->w = w;
+		td->h = h;
+	}
+
+	/* Sort the array and expose it through the tileset->tiledefs pointer. */
+	qsort(ctx->tf->tiledefs.data, ctx->tf->tiledefs.size, ctx->tf->tiledefs.elemsize, tileset_tiledef_cmp);
+	ctx->tileset->tiledefs = ctx->tf->tiledefs.data;
+	ctx->tileset->tiledefsz = ctx->tf->tiledefs.size;
+
+	return 0;
+}
+
+static int
+parse_animations(struct context *ctx, const char *line)
+{
+	(void)line;
+
+	unsigned short id;
+	unsigned int delay;
+	char filename[FILENAME_MAX + 1];
+	struct tileset_animation_block *anim;
+
+	alloc_pool_init(&ctx->tf->anims[0], sizeof (struct tileset_animation_block), tileset_animation_block_finish);
+	alloc_pool_init(&ctx->tf->anims[1], sizeof (struct tileset_animation), NULL);
+
+	/*
+	 * 1. Create the first array of animation, sprite and texture that are
+	 *    owned by the tileset_file structure.
+	 */
+	while (fscanf(ctx->fp, "%hu|" MAX_F(FILENAME_MAX) "|%u", &id, filename, &delay) == 3) {
+		anim = alloc_pool_new(&ctx->tf->anims[0]);
+
+		if (image_open(&anim->texture, util_pathf("%s/%s", ctx->basedir, filename)) < 0)
+			return -1;
+
+		sprite_init(&anim->sprite, &anim->texture, ctx->tilewidth, ctx->tileheight);
+		animation_init(&anim->animation, &anim->sprite, delay);
+	}
+
+	/*
+	 * 2. Create the second array that only consist of pointers to
+	 *    animations referencing the first array.
+	 */
+	for (size_t i = 0; i < ctx->tf->anims[0].size; ++i) {
+		struct tileset_animation_block *anim = alloc_pool_get(&ctx->tf->anims[0], i);
+		struct tileset_animation *ta;
+
+		if (!(ta = alloc_pool_new(&ctx->tf->anims[1])))
+			return -1;
+
+		ta->id = id;
+		ta->animation = &anim->animation;
+	}
+
+	/*
+	 * 3. Finally expose the second array through the tileset->anims pointer
+	 *    and sort it.
+	 */
+	qsort(ctx->tf->anims[1].data, ctx->tf->anims[1].size, ctx->tf->anims[1].elemsize, tileset_animation_cmp);
+	ctx->tileset->anims  = ctx->tf->anims[1].data;
+	ctx->tileset->animsz = ctx->tf->anims[1].size;
+
+	return 0;
+}
+
+static int
+parse_image(struct context *ctx, const char *line)
+{
+	char *p;
+
+	if (ctx->tilewidth == 0 || ctx->tileheight == 0)
+		return errorf("missing tile dimensions before image");
+	if (!(p = strchr(line, '|')))
+		return errorf("could not parse image");
+
+	if (image_open(&ctx->tf->image, util_pathf("%s/%s", ctx->basedir, p + 1)) < 0)
+		return -1;
+
+	sprite_init(&ctx->tf->sprite, &ctx->tf->image, ctx->tilewidth, ctx->tileheight);
+	ctx->tileset->sprite = &ctx->tf->sprite;
+
+	return 0;
+}
+
+static int
+parse_line(struct context *ctx, const char *line)
+{
+	static const struct {
+		const char *property;
+		int (*read)(struct context *, const char *);
+	} props[] = {
+		{ "tilewidth",  parse_tilewidth         },
+		{ "tileheight", parse_tileheight        },
+		{ "tiledefs",   parse_tiledefs          },
+		{ "animations", parse_animations        },
+		{ "image",      parse_image             }
+	};
+
+	for (size_t i = 0; i < UTIL_SIZE(props); ++i) {
+		if (strncmp(line, props[i].property, strlen(props[i].property)) == 0)
+			return props[i].read(ctx, line);
+	}
+
+	return 0;
+}
+
+static int
+parse(struct context *ctx, const char *path)
+{
+	char line[1024];
+	char basedir[PATH_MAX];
+
+	util_strlcpy(basedir, path, sizeof (basedir));
+	util_strlcpy(ctx->basedir, util_dirname(basedir), sizeof (ctx->basedir));
+
+	while (fgets(line, sizeof (line), ctx->fp)) {
+		/* Remove \n if any */
+		line[strcspn(line, "\r\n")] = '\0';
+
+		if (parse_line(ctx, line) < 0)
+			return -1;
+	}
+
+	return 0;
+}
+
+static int
+check(const struct tileset *tileset)
+{
+	if (!tileset->sprite)
+		return errorf("missing tileset image");
+
+	return 0;
+}
+
+int
+tileset_file_open(struct tileset_file *tf, struct tileset *tileset, const char *path)
+{
+	assert(tf);
+	assert(tileset);
+	assert(path);
+
+	struct context ctx = {
+		.tf = tf,
+		.tileset = tileset
+	};
+	int ret = 0;
+
+	memset(tileset, 0, sizeof (*tileset));
+
+	if (!(ctx.fp = fopen(path, "r")))
+		return -1;
+	if ((ret = parse(&ctx, path)) < 0 || (ret = check(tileset)) < 0)
+		tileset_file_finish(tf);
+
+	fclose(ctx.fp);
+
+	return ret;
+}
+
+void
+tileset_file_finish(struct tileset_file *tf)
+{
+	assert(tf);
+
+	alloc_pool_finish(&tf->tiledefs);
+	alloc_pool_finish(&tf->anims[0]);
+	alloc_pool_finish(&tf->anims[1]);
+
+	texture_finish(&tf->image);
+
+	memset(tf, 0, sizeof (*tf));
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-rpg/mlk/rpg/tileset-file.h	Sat Oct 15 21:24:17 2022 +0200
@@ -0,0 +1,49 @@
+/*
+ * tileset-file.h -- tileset file loader
+ *
+ * Copyright (c) 2020-2022 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 MLK_RPG_TILESET_FILE_H
+#define MLK_RPG_TILESET_FILE_H
+
+#include <stddef.h>
+
+#include <mlk/core/alloc.h>
+#include <mlk/core/core.h>
+#include <mlk/core/sprite.h>
+#include <mlk/core/texture.h>
+
+struct tileset;
+struct tileset_tiledef;
+
+struct tileset_file {
+	struct alloc_pool tiledefs;
+	struct alloc_pool anims[2];
+	struct texture image;
+	struct sprite sprite;
+};
+
+CORE_BEGIN_DECLS
+
+int
+tileset_file_open(struct tileset_file *, struct tileset *, const char *);
+
+void
+tileset_file_finish(struct tileset_file *);
+
+CORE_END_DECLS
+
+#endif /* !MLK_RPG_TILESET_FILE_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-rpg/mlk/rpg/tileset.c	Sat Oct 15 21:24:17 2022 +0200
@@ -0,0 +1,93 @@
+/*
+ * tileset.c -- map tileset definition
+ *
+ * Copyright (c) 2020-2022 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 <stdlib.h>
+
+#include <mlk/core/animation.h>
+#include <mlk/core/sprite.h>
+
+#include "tileset.h"
+
+static inline int
+anim_cmp(const void *d1, const void *d2)
+{
+	const struct tileset_animation *mtd1 = d1;
+	const struct tileset_animation *mtd2 = d2;
+
+	if (mtd1->id < mtd2->id)
+		return -1;
+	if (mtd1->id > mtd2->id)
+		return 1;
+
+	return 0;
+}
+
+static inline const struct tileset_animation *
+find(const struct tileset *ts, unsigned int r, unsigned int c)
+{
+	const struct tileset_animation key = {
+		.id = c + (r * ts->sprite->ncols)
+	};
+
+	return bsearch(&key, ts->anims, ts->animsz, sizeof (key), anim_cmp);
+}
+
+int
+tileset_ok(const struct tileset *ts)
+{
+	return ts && sprite_ok(ts->sprite);
+}
+
+void
+tileset_start(struct tileset *ts)
+{
+	for (size_t i = 0; i < ts->animsz; ++i) {
+		struct tileset_animation *ta = &ts->anims[i];
+
+		if (ta->animation)
+			animation_start(ta->animation);
+	}
+}
+
+void
+tileset_update(struct tileset *ts, unsigned int ticks)
+{
+	for (size_t i = 0; i < ts->animsz; ++i) {
+		struct tileset_animation *ta = &ts->anims[i];
+
+		if (!ta->animation)
+			continue;
+
+		if (animation_update(ta->animation, ticks))
+			animation_start(ta->animation);
+	}
+}
+
+void
+tileset_draw(const struct tileset *ts, unsigned int r, unsigned int c, int x, int y)
+{
+	assert(ts);
+
+	const struct tileset_animation *ta;
+
+	if ((ta = find(ts, r, c)))
+		animation_draw(ta->animation, x, y);
+	else
+		sprite_draw(ts->sprite, r, c, x, y);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-rpg/mlk/rpg/tileset.h	Sat Oct 15 21:24:17 2022 +0200
@@ -0,0 +1,65 @@
+/*
+ * tileset.h -- map tileset definition
+ *
+ * Copyright (c) 2020-2022 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 MLK_RPG_TILESET_H
+#define MLK_RPG_TILESET_H
+
+#include <stddef.h>
+
+#include <mlk/core/core.h>
+
+struct sprite;
+
+struct tileset_tiledef {
+	unsigned short id;
+	short x;
+	short y;
+	unsigned short w;
+	unsigned short h;
+};
+
+struct tileset_animation {
+	unsigned short id;
+	struct animation *animation;
+};
+
+struct tileset {
+	struct tileset_tiledef *tiledefs;
+	size_t tiledefsz;
+	struct tileset_animation *anims;
+	size_t animsz;
+	struct sprite *sprite;
+};
+
+CORE_BEGIN_DECLS
+
+int
+tileset_ok(const struct tileset *);
+
+void
+tileset_start(struct tileset *);
+
+void
+tileset_update(struct tileset *, unsigned int);
+
+void
+tileset_draw(const struct tileset *, unsigned int, unsigned int, int, int);
+
+CORE_END_DECLS
+
+#endif /* !MLK_RPG_TILESET_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-rpg/mlk/rpg/walksprite.c	Sat Oct 15 21:24:17 2022 +0200
@@ -0,0 +1,69 @@
+/*
+ * walksprite.c -- sprite designed for walking entities
+ *
+ * Copyright (c) 2020-2022 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 <string.h>
+
+#include <mlk/core/sprite.h>
+
+#include "walksprite.h"
+
+void
+walksprite_init(struct walksprite *ws, struct sprite *sprite, unsigned int delay)
+{
+	assert(ws);
+	assert(sprite);
+
+	memset(ws, 0, sizeof (*ws));
+	ws->sprite = sprite;
+	ws->delay = delay;
+}
+
+void
+walksprite_reset(struct walksprite *ws)
+{
+	assert(ws);
+
+	ws->index = 0;
+}
+
+void
+walksprite_update(struct walksprite *ws, unsigned int ticks)
+{
+	assert(ws);
+
+	ws->elapsed += ticks;
+
+	if (ws->elapsed >= ws->delay) {
+		ws->index += 1;
+
+		if (ws->index >= ws->sprite->ncols)
+			ws->index = 0;
+
+		ws->elapsed = 0;
+	}
+}
+
+void
+walksprite_draw(const struct walksprite *ws, unsigned int orientation, int x, int y)
+{
+	assert(ws);
+	assert(orientation < 8);
+
+	sprite_draw(ws->sprite, orientation, ws->index, x, y);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-rpg/mlk/rpg/walksprite.h	Sat Oct 15 21:24:17 2022 +0200
@@ -0,0 +1,79 @@
+/*
+ * walksprite.h -- sprite designed for walking entities
+ *
+ * Copyright (c) 2020-2022 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 MLK_RPG_WALKSPRITE_H
+#define MLK_RPG_WALKSPRITE_H
+
+#include <mlk/core/core.h>
+
+struct sprite;
+
+/**
+ * \brief Sprite designed for walking entities.
+ *
+ * This structure works with sprite images that are defined as using the
+ * following conventions:
+ *
+ * ```
+ * 7   0   1
+ *   ↖ ↑ ↗
+ * 6 ←   → 2
+ *   ↙ ↓ ↘
+ * 5   4   3
+ * ```
+ *
+ * Where numbers define row in the sprite according to the character
+ * orientation. In other terms, your image sprite should look like this:
+ *
+ * ```
+ * row columns in your image
+ * ---|---------------------
+ *  0 | ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
+ *  1 | ↗↗↗↗↗↗↗↗↗↗↗↗↗↗↗↗↗↗↗↗
+ *  2 | →→→→→→→→→→→→→→→→→→→→
+ *  3 | ↘↘↘↘↘↘↘↘↘↘↘↘↘↘↘↘↘↘↘↘
+ *  4 | ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
+ *  5 | ↙↙↙↙↙↙↙↙↙↙↙↙↙↙↙↙↙↙↙↙
+ *  6 | ←←←←←←←←←←←←←←←←←←←←
+ *  7 | ↖↖↖↖↖↖↖↖↖↖↖↖↖↖↖↖↖↖↖↖
+ * ```
+ */
+struct walksprite {
+	struct sprite *sprite;
+	unsigned int delay;
+	unsigned int index;
+	unsigned int elapsed;
+};
+
+CORE_BEGIN_DECLS
+
+void
+walksprite_init(struct walksprite *, struct sprite *, unsigned int);
+
+void
+walksprite_reset(struct walksprite *);
+
+void
+walksprite_update(struct walksprite *, unsigned int);
+
+void
+walksprite_draw(const struct walksprite *, unsigned int, int, int);
+
+CORE_END_DECLS
+
+#endif /* !MLK_RPG_WALKSPRITE_H */
--- a/src/libmlk-rpg/assets/sql/character-load.sql	Sat Oct 15 21:19:25 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,30 +0,0 @@
---
--- character-load.sql -- load a character data
---
--- Copyright (c) 2020-2022 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 = ?
--- a/src/libmlk-rpg/assets/sql/character-save.sql	Sat Oct 15 21:19:25 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,44 +0,0 @@
---
--- character-save.sql -- save or replace character
---
--- Copyright (c) 2020-2022 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(
-	?,
-	?,
-	?,
-	?,
-	?,
-	?,
-	?,
-	?,
-	?,
-	?,
-	?
-)
--- a/src/libmlk-rpg/assets/sql/init.sql	Sat Oct 15 21:19:25 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,55 +0,0 @@
---
--- init.sql -- initialize database
---
--- Copyright (c) 2020-2022 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
-);
-
-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'));
-
-COMMIT;
--- a/src/libmlk-rpg/assets/sql/property-load.sql	Sat Oct 15 21:19:25 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,21 +0,0 @@
---
--- property-load.sql -- get a property
---
--- Copyright (c) 2020-2022 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/src/libmlk-rpg/assets/sql/property-remove.sql	Sat Oct 15 21:19:25 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,21 +0,0 @@
---
--- property-remove.sql -- remove a property
---
--- Copyright (c) 2020-2022 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/src/libmlk-rpg/assets/sql/property-save.sql	Sat Oct 15 21:19:25 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,26 +0,0 @@
---
--- property-save.sql -- set a property
---
--- Copyright (c) 2020-2022 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/src/libmlk-rpg/assets/sql/quest-remove.sql	Sat Oct 15 21:19:25 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,21 +0,0 @@
---
--- quest-remove.sql -- remove a quest entirely
---
--- Copyright (c) 2020-2022 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 = ?
--- a/src/libmlk-rpg/assets/sql/quest-save.sql	Sat Oct 15 21:19:25 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,24 +0,0 @@
---
--- quest-save.sql -- create parent quest entry
---
--- Copyright (c) 2020-2022 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(
-	?
-)
--- a/src/libmlk-rpg/assets/sql/quest-step-load.sql	Sat Oct 15 21:19:25 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,21 +0,0 @@
---
--- quest-step-load.sql -- remove a quest entirely
---
--- Copyright (c) 2020-2022 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 = ?
--- a/src/libmlk-rpg/assets/sql/quest-step-save.sql	Sat Oct 15 21:19:25 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,27 +0,0 @@
---
--- quest-step-save.sql -- save a quest step
---
--- Copyright (c) 2020-2022 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(
-	?,
-	?,
-	?
-)
--- a/src/libmlk-rpg/rpg/battle-bar-default.c	Sat Oct 15 21:19:25 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,693 +0,0 @@
-/*
- * battle-bar-default.c -- default battle status bar and menu implementation
- *
- * Copyright (c) 2020-2022 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 <stdlib.h>
-#include <string.h>
-
-#include <mlk/core/alloc.h>
-#include <mlk/core/event.h>
-#include <mlk/core/font.h>
-#include <mlk/core/sprite.h>
-#include <mlk/core/trace.h>
-#include <mlk/core/util.h>
-#include <mlk/core/window.h>
-
-#include <mlk/ui/align.h>
-#include <mlk/ui/theme.h>
-
-#include "battle-bar-default.h"
-#include "battle-bar.h"
-#include "battle-state-item.h"
-#include "battle-state-selection.h"
-#include "battle.h"
-#include "character.h"
-#include "inventory.h"
-#include "item.h"
-#include "spell.h"
-
-#define THEME(bar) ((bar)->theme ? (bar)->theme : theme_default())
-
-struct geo {
-	int x, y;
-	unsigned int w, h;
-};
-
-static inline void
-dimensions(struct geo geo[2], const struct battle_bar_default *bar)
-{
-	/* 0 == main menu */
-	geo[0].w = bar->w * 0.2;
-	geo[0].h = bar->h;
-	geo[0].x = bar->x + (bar->w / 2) - (geo[0].w / 2);
-	geo[0].y = window.h - bar->h;
-
-	/* 1 == status frame */
-	geo[1].x = geo[0].x + geo[0].w;
-	geo[1].y = geo[0].y;
-	geo[1].w = (bar->w - geo[0].w) / 2;
-	geo[1].h = bar->h;
-}
-
-/*
- * The following validate_* functions are called when the user has validated a
- * selection depending on the current menu (e.g. Magic, Items).
- *
- * They change the battle state to the appropriate one.
- */
-
-static void
-validate_attack(struct battle_bar_default *bar, struct battle *bt, const struct selection *sel)
-{
-	(void)bar;
-
-	struct character *target;
-
-	if (sel->index_side == 0)
-		target = bt->enemies[sel->index_character]->ch;
-	else
-		target = bt->team[sel->index_character]->ch;
-
-	battle_attack(bt, battle_current(bt)->ch, target);
-}
-
-static void
-validate_magic(struct battle_bar_default *bar, struct battle *bt, const struct selection *sel)
-{
-	struct character *source = battle_current(bt)->ch;
-	const struct spell *spell = source->spells[bar->grid.selected];
-
-	battle_cast(bt, source, spell, sel);
-}
-
-static void
-validate_item(struct battle_bar_default *bar, struct battle *bt, const struct selection *sel)
-{
-	struct inventory_slot *slot;
-	struct battle_entity *source, *target;
-
-	if (bar->grid.selected >= INVENTORY_ITEM_MAX)
-		return;
-	if (!(slot = &bt->inventory->items[bar->grid.selected]))
-		return;
-
-	source = battle_current(bt);
-	target = sel->index_side == 0
-		? bt->enemies[sel->index_character]
-		: bt->team[sel->index_character];
-
-	/* TODO: battle_use? */
-	battle_state_item(bt, source, target, slot);
-}
-
-/*
- * The following functions are used to switch to the battle selection state
- * using the appropriate selector algorithm. For example, an item can only be
- * used on a unique target while a spell can have multiple choices.
- */
-
-static void
-switch_selection_attack(struct battle_bar_default *bar, struct battle *bt)
-{
-	struct selection sel = {
-		.allowed_kinds = SELECTION_KIND_ONE,
-		.allowed_sides = SELECTION_SIDE_ENEMY,
-		.index_side = 0
-	};
-
-	/* Disable handling anymore. */
-	bar->state = BATTLE_BAR_DEFAULT_STATE_NONE;
-
-	selection_first(&sel, bt);
-	battle_state_selection(bt, &sel);
-}
-
-static void
-switch_selection_spell(struct battle_bar_default *bar, struct battle *bt)
-{
-	const struct character *ch = battle_current(bt)->ch;
-	const struct spell *sp = ch->spells[bar->grid.selected];
-	struct selection sel = {0};
-
-	if (bar->grid.selected > CHARACTER_SPELL_MAX)
-		return;
-	if (!(sp = ch->spells[bar->grid.selected]) || sp->mp > (unsigned int)ch->mp)
-		return;
-
-	/* Use the spell selection algorithm to fill default values. */
-	spell_select(sp, bt, &sel);
-	battle_state_selection(bt, &sel);
-
-	/* A cursor should be present. */
-	if (!sprite_ok(BATTLE_THEME(bt)->sprites[THEME_SPRITE_CURSOR]))
-		tracef("battle: no cursor sprite in theme");
-}
-
-static void
-switch_selection_item(struct battle *bt)
-{
-	const struct selection slt = {
-		.allowed_kinds = SELECTION_KIND_ONE,
-		.allowed_sides = SELECTION_SIDE_TEAM | SELECTION_SIDE_ENEMY,
-		.index_side = 1,
-		.index_character = battle_index(bt)
-	};
-
-	battle_state_selection(bt, &slt);
-}
-
-/*
- * The following functions actually draw the bar and their components depending
- * on the current selected menu.
- */
-
-static void
-draw_help(const struct battle_bar_default *bar, const char *what)
-{
-	struct label label = {0};
-	unsigned int lw = 0, lh = 0;
-
-	label.flags = LABEL_FLAGS_SHADOW;
-	label.text = what;
-	label_query(&label, &lw, &lh);
-	label.x = bar->grid.x + (bar->grid.w / 2) - (lw / 2);
-	label.y = bar->grid.y - lh - THEME(bar)->padding;
-	label_draw(&label);
-}
-
-static void
-draw_spell_help(const struct battle_bar_default *bar, const struct battle *bt)
-{
-	const struct character *ch = battle_current(bt)->ch;
-	const struct spell *sp;
-
-	if (bar->grid.selected >= CHARACTER_SPELL_MAX)
-		return;
-	if (!(sp = ch->spells[bar->grid.selected]))
-		return;
-
-	draw_help(bar, sp->description);
-}
-
-static void
-draw_item_help(const struct battle_bar_default *bar, const struct battle *bt)
-{
-	const struct inventory_slot *slot;
-
-	if (bar->grid.selected >= INVENTORY_ITEM_MAX)
-		return;
-
-	slot = &bt->inventory->items[bar->grid.selected];
-
-	if (!slot->item)
-		return;
-
-	draw_help(bar, slot->item->description);
-}
-
-static void
-draw_status_character_stats(const struct battle_bar_default *bar,
-                            const struct character *ch,
-                            int x,
-                            int y,
-                            unsigned int h)
-{
-	const struct theme *theme = THEME(bar);
-	struct label label;
-	unsigned int spacing, lw, lh;
-	char line[64];
-
-	/* Compute spacing between elements. */
-	spacing = h - (font_height(theme->fonts[THEME_FONT_INTERFACE]) * 3);
-	spacing /= 4;
-
-	/* Reuse the same label. */
-	label.theme = theme;
-	label.text = line;
-	label.flags = LABEL_FLAGS_SHADOW;
-
-	/* Name. */
-	snprintf(line, sizeof (line), "%s", ch->name);
-	label_query(&label, &lw, &lh);
-	label.x = x + theme->padding;
-	label.y = y + spacing;
-	label_draw(&label);
-
-	/* HP. */
-	snprintf(line, sizeof (line), "%d/%u", ch->hp, ch->hpmax);
-	label_query(&label, &lw, &lh);
-	label.x = x + theme->padding;
-	label.y = label.y + lh + spacing;
-	label_draw(&label);
-
-	/* MP. */
-	snprintf(line, sizeof (line), "%d/%u", ch->mp, ch->mpmax);
-	label_query(&label, &lw, &lh);
-	label.x = x + theme->padding;
-	label.y = label.y + lh + spacing;
-	label_draw(&label);
-
-	/* Status. */
-	/* TODO: list all status. */
-}
-
-static void
-draw_status_character(const struct battle_bar_default *bar,
-                      const struct battle *bt,
-                      const struct character *ch,
-                      const struct geo *geo,
-                      unsigned int index)
-{
-	int x, y;
-	unsigned int w, h;
-
-	/* Compute bounding box for rendering. */
-	w = geo->w / bt->teamsz;
-	h = geo->h;
-	x = geo->x + (index * w);
-	y = geo->y;
-
-	draw_status_character_stats(bar, ch, x, y, h);
-}
-
-static void
-draw_status_characters(const struct battle_bar_default *bar,
-                       const struct battle *bt,
-                       const struct geo *geo)
-{
-	const struct battle_entity *et;
-	unsigned int index = 0;
-
-	BATTLE_TEAM_FOREACH(bt, et) {
-		if (character_ok(et->ch))
-			draw_status_character(bar, bt, et->ch, geo, index);
-
-		++index;
-	}
-}
-
-static void
-draw_status(const struct battle_bar_default *bar, const struct battle *bt, const struct geo *geo)
-{
-	frame_draw(&(const struct frame) {
-		.x = geo->x,
-		.y = geo->y,
-		.w = geo->w,
-		.h = geo->h
-	});
-
-	draw_status_characters(bar, bt, geo);
-}
-
-static void
-draw_menu(const struct battle_bar_default *bar, const struct geo *geo)
-{
-	struct {
-		unsigned int w, h;
-		enum align align;
-		struct label label;
-	} buttons[] = {
-		{
-			.align = ALIGN_TOP,
-			.label = {
-				.text = "Attack",
-				.flags = LABEL_FLAGS_SHADOW
-			}
-		},
-		{
-			.align = ALIGN_RIGHT,
-			.label = {
-				.text = "Magic",
-				.flags = LABEL_FLAGS_SHADOW
-			}
-		},
-		{
-			.align = ALIGN_BOTTOM,
-			.label = {
-				.text = "Objects",
-				.flags = LABEL_FLAGS_SHADOW
-			}
-		},
-		{
-			.align = ALIGN_LEFT,
-			.label = {
-				.text = "Special",
-				.flags = LABEL_FLAGS_SHADOW
-			}
-		}
-	};
-
-	const struct theme *theme = THEME(bar);
-	int bx, by;
-	unsigned int bw, bh;
-
-	/* Compute bounding box with margins removed. */
-	bx = geo->x + theme->padding;
-	by = geo->y + theme->padding;
-	bw = geo->w - theme->padding * 2;
-	bh = geo->h - theme->padding * 2;
-
-	/* Draw menu frame. */
-	frame_draw(&(const struct frame) {
-		.x = geo->x,
-		.y = geo->y,
-		.w = geo->w,
-		.h = geo->h
-	});
-
-	for (size_t i = 0; i < UTIL_SIZE(buttons); ++i) {
-		buttons[i].label.theme = theme;
-
-		label_query(&buttons[i].label, &buttons[i].w, &buttons[i].h);
-
-		/* Change theme if it's selected. */
-		if ((size_t)bar->menu == i)
-			buttons[i].label.flags |=  LABEL_FLAGS_SELECTED;
-		else
-			buttons[i].label.flags &= ~LABEL_FLAGS_SELECTED;
-
-		align(buttons[i].align,
-		    &buttons[i].label.x, &buttons[i].label.y, buttons[i].w, buttons[i].h,
-		    bx, by, bw, bh);
-		label_draw(&buttons[i].label);
-	}
-}
-
-/*
- * This function is called only in the first level of the bar menu: selecting
- * one of the Attack, Magic, Item and Special items.
- */
-static void
-handle_keydown_menu(struct battle_bar_default *bar, struct battle *bt, const union event *ev)
-{
-	(void)bt;
-
-	switch (ev->key.key) {
-	case KEY_UP:
-		bar->menu = BATTLE_BAR_DEFAULT_MENU_ATTACK;
-		break;
-	case KEY_RIGHT:
-		bar->menu = BATTLE_BAR_DEFAULT_MENU_MAGIC;
-		break;
-	case KEY_DOWN:
-		bar->menu = BATTLE_BAR_DEFAULT_MENU_ITEM;
-		break;
-	case KEY_LEFT:
-		bar->menu = BATTLE_BAR_DEFAULT_MENU_SPECIAL;
-		break;
-	case KEY_ENTER:
-		/*
-		 * At this step, attack does not require opening the sub menu so
-		 * we change selection state immediately if needed.
-		 */
-		switch (bar->menu) {
-		case BATTLE_BAR_DEFAULT_MENU_ATTACK:
-			switch_selection_attack(bar, bt);
-			break;
-		case BATTLE_BAR_DEFAULT_MENU_ITEM:
-			battle_bar_default_open_item(bar, bt);
-			break;
-		case BATTLE_BAR_DEFAULT_MENU_MAGIC:
-			battle_bar_default_open_magic(bar, bt, battle_current(bt)->ch);
-			break;
-		default:
-			break;
-		}
-		break;
-	default:
-		break;
-	}
-}
-
-/*
- * This function is called when we're selecting a submenu entry from Items
- * and Magic.
- */
-static void
-handle_keydown_grid(struct battle_bar_default *bar, struct battle *bt, const union event *ev)
-{
-	/* Go back to main menu if I press escape. */
-	if (ev->key.key == KEY_ESCAPE)
-		bar->state = BATTLE_BAR_DEFAULT_STATE_MENU;
-	else if (gridmenu_handle(&bar->grid, ev)) {
-		switch (bar->menu) {
-		case BATTLE_BAR_DEFAULT_MENU_MAGIC:
-			switch_selection_spell(bar, bt);
-			break;
-		case BATTLE_BAR_DEFAULT_MENU_ITEM:
-			switch_selection_item(bt);
-			break;
-		default:
-			break;
-		}
-	}
-}
-
-static void
-handle_keydown(struct battle_bar_default *bar, struct battle *bt, const union event *ev)
-{
-	assert(ev->type == EVENT_KEYDOWN);
-
-	static void (*handlers[])(struct battle_bar_default *, struct battle *, const union event *) = {
-		[BATTLE_BAR_DEFAULT_STATE_MENU] = handle_keydown_menu,
-		[BATTLE_BAR_DEFAULT_STATE_GRID] = handle_keydown_grid
-	};
-
-	if (handlers[bar->state])
-		handlers[bar->state](bar, bt, ev);
-}
-
-#if 0
-
-static void
-handle_clickdown(struct battle_bar_default *bar, struct battle *bt, const union event *ev)
-{
-	assert(ev->type == EVENT_CLICKDOWN);
-
-	(void)bar;
-	(void)bt;
-
-	switch (bar->state) {
-	case BATTLE_BAR_DEFAULT_STATE_MENU:
-		/* We are selecting a main menu entry. */
-		/* TODO: implement click here too. */
-		break;
-	case BATTLE_BAR_DEFAULT_STATE_SUB:
-		/* We are in the sub menu (objects/spells). */
-		if (bar->sub_grid.state == GRIDMENU_STATE_ACTIVATED)
-	default:
-		break;
-	}
-
-	return 0;
-}
-
-#endif
-
-static void
-self_start(struct battle_bar *bar, struct battle *bt)
-{
-	(void)bt;
-
-	battle_bar_default_start(bar->data);
-}
-
-static void
-self_select(struct battle_bar *bar, struct battle *bt, const struct selection *sel)
-{
-	battle_bar_default_select(bar->data, bt, sel);
-}
-
-static void
-self_handle(struct battle_bar *bar, struct battle *bt, const union event *ev)
-{
-	battle_bar_default_handle(bar->data, bt, ev);
-}
-
-static void
-self_draw(const struct battle_bar *bar, const struct battle *bt)
-{
-	battle_bar_default_draw(bar->data, bt);
-}
-
-void
-battle_bar_default_init(struct battle_bar_default *bar)
-{
-	assert(bar);
-
-	struct geo geo[2];
-
-	memset(bar, 0, sizeof (*bar));
-
-	bar->w = window.w;
-	bar->h = window.h * 0.12;
-	bar->x = 0;
-	bar->y = window.h - bar->h;
-
-	dimensions(geo, bar);
-
-	gridmenu_init(&bar->grid, 2, 2, NULL, 0);
-	gridmenu_resize(&bar->grid, bar->x, geo[0].y, geo[1].w, bar->h);
-	bar->grid.theme = bar->theme;
-}
-
-void
-battle_bar_default_open_magic(struct battle_bar_default *bar, const struct battle *bt, struct character *ch)
-{
-	assert(bar);
-	assert(bt);
-	assert(ch);
-
-	(void)bt;
-
-	bar->items = alloc_rearray0(bar->items, bar->itemsz,
-	    CHARACTER_SPELL_MAX, sizeof (*bar->items));
-	bar->itemsz = CHARACTER_SPELL_MAX;
-	bar->state = BATTLE_BAR_DEFAULT_STATE_GRID;
-
-	for (size_t i = 0; i < CHARACTER_SPELL_MAX; ++i)
-		if (ch->spells[i])
-			bar->items[i] = ch->spells[i]->name;
-
-	bar->grid.items = bar->items;
-	bar->grid.itemsz = bar->itemsz;
-}
-
-void
-battle_bar_default_open_item(struct battle_bar_default *bar, const struct battle *bt)
-{
-	assert(bar);
-	assert(bt);
-
-	/* TODO: not implemented yet. */
-	(void)bar;
-	(void)bt;
-#if 0
-	for (size_t i = 0; i < INVENTORY_ITEM_MAX; ++i) {
-		if (bt->inventory->items[i].item) {
-			snprintf(bar->sub_items[i], sizeof (bar->sub_items[i]), "%-16s %u",
-			    bt->inventory->items[i].item->name, bt->inventory->items[i].amount);
-			bar->sub_grid.menu[i] = bar->sub_items[i];
-		}
-	}
-
-	bar->state = BATTLE_BAR_DEFAULT_STATE_GRID;
-#endif
-}
-
-void
-battle_bar_default_start(struct battle_bar_default *bar)
-{
-	assert(bar);
-
-	bar->menu = BATTLE_BAR_DEFAULT_MENU_ATTACK;
-	bar->state = BATTLE_BAR_DEFAULT_STATE_MENU;
-}
-
-/*
- * Apply the battle selection for the current menu item. This function is called
- * from the battle-state-selection state when the user validated the selection.
- */
-void
-battle_bar_default_select(struct battle_bar_default *bar, struct battle *bt, const struct selection *sel)
-{
-	assert(bar);
-	assert(bt);
-	assert(sel);
-
-	static void (*validate[])(struct battle_bar_default *, struct battle *, const struct selection *) = {
-		[BATTLE_BAR_DEFAULT_MENU_ATTACK]        = validate_attack,
-		[BATTLE_BAR_DEFAULT_MENU_ITEM]          = validate_item,
-		[BATTLE_BAR_DEFAULT_MENU_MAGIC]         = validate_magic,
-		[BATTLE_BAR_DEFAULT_MENU_SPECIAL]       = NULL
-	};
-
-	if (validate[bar->menu])
-		validate[bar->menu](bar, bt, sel);
-}
-
-void
-battle_bar_default_handle(struct battle_bar_default *bar, struct battle *bt, const union event *ev)
-{
-	assert(bar);
-	assert(bt);
-	assert(ev);
-
-	static void (*handlers[])(struct battle_bar_default *, struct battle *, const union event *) = {
-		[EVENT_KEYDOWN] = handle_keydown,
-		[EVENT_NUM] = NULL
-	};
-
-	if (handlers[ev->type])
-		handlers[ev->type](bar, bt, ev);
-}
-
-void
-battle_bar_default_draw(const struct battle_bar_default *bar, const struct battle *bt)
-{
-	assert(bar);
-	assert(bt);
-
-	struct geo geo[2];
-
-	dimensions(geo, bar);
-	draw_menu(bar, &geo[0]);
-	draw_status(bar, bt, &geo[1]);
-
-	if (bar->state == BATTLE_BAR_DEFAULT_STATE_GRID) {
-		switch (bar->menu) {
-		case BATTLE_BAR_DEFAULT_MENU_MAGIC:
-			draw_spell_help(bar, bt);
-			break;
-		case BATTLE_BAR_DEFAULT_MENU_ITEM:
-			draw_item_help(bar, bt);
-			break;
-		default:
-			break;
-		}
-	}
-
-	/* Sub menu is only shown if state is set to it. */
-	if (bar->state == BATTLE_BAR_DEFAULT_STATE_GRID)
-		gridmenu_draw(&bar->grid);
-}
-
-void
-battle_bar_default_finish(struct battle_bar_default *bar)
-{
-	assert(bar);
-
-	free(bar->items);
-	memset(bar, 0, sizeof (*bar));
-}
-
-void
-battle_bar_default(struct battle_bar_default *self, struct battle_bar *bar)
-{
-	assert(self);
-	assert(bar);
-
-	memset(bar, 0, sizeof (*bar));
-
-	bar->data = self;
-	bar->start = self_start;
-	bar->select = self_select;
-	bar->handle = self_handle;
-	bar->draw = self_draw;
-}
--- a/src/libmlk-rpg/rpg/battle-bar-default.h	Sat Oct 15 21:19:25 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,93 +0,0 @@
-/*
- * battle-bar-default.h -- default battle status bar and menu implementation
- *
- * Copyright (c) 2020-2022 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 MLK_RPG_BATTLE_BAR_DEFAULT_H
-#define MLK_RPG_BATTLE_BAR_DEFAULT_H
-
-#include <mlk/core/core.h>
-
-#include <mlk/ui/gridmenu.h>
-
-struct battle;
-struct battle_bar;
-struct character;
-struct selection;
-struct theme;
-
-union event;
-
-enum battle_bar_default_menu {
-	BATTLE_BAR_DEFAULT_MENU_ATTACK,
-	BATTLE_BAR_DEFAULT_MENU_MAGIC,
-	BATTLE_BAR_DEFAULT_MENU_ITEM,
-	BATTLE_BAR_DEFAULT_MENU_SPECIAL
-};
-
-enum battle_bar_default_state {
-	BATTLE_BAR_DEFAULT_STATE_NONE,
-	BATTLE_BAR_DEFAULT_STATE_MENU,
-	BATTLE_BAR_DEFAULT_STATE_GRID
-};
-
-struct battle_bar_default {
-	int x;
-	int y;
-	unsigned int w;
-	unsigned int h;
-	struct theme *theme;
-	enum battle_bar_default_state state;
-	enum battle_bar_default_menu menu;
-
-	/* Private fields. */
-	const char **items;
-	size_t itemsz;
-	struct gridmenu grid;
-};
-
-CORE_BEGIN_DECLS
-
-void
-battle_bar_default_init(struct battle_bar_default *);
-
-void
-battle_bar_default_open_magic(struct battle_bar_default *, const struct battle *, struct character *);
-
-void
-battle_bar_default_open_item(struct battle_bar_default *, const struct battle *);
-
-void
-battle_bar_default_start(struct battle_bar_default *);
-
-void
-battle_bar_default_select(struct battle_bar_default *, struct battle *, const struct selection *);
-
-void
-battle_bar_default_handle(struct battle_bar_default *, struct battle *, const union event *);
-
-void
-battle_bar_default_draw(const struct battle_bar_default *, const struct battle *);
-
-void
-battle_bar_default_finish(struct battle_bar_default *);
-
-void
-battle_bar_default(struct battle_bar_default *, struct battle_bar *);
-
-CORE_END_DECLS
-
-#endif /* !MLK_RPG_BATTLE_BAR_DEFAULT_H */
--- a/src/libmlk-rpg/rpg/battle-bar.c	Sat Oct 15 21:19:25 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,84 +0,0 @@
-/*
- * battle-bar.c -- abstract battle bar
- *
- * Copyright (c) 2020-2022 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 "battle-bar.h"
-
-void
-battle_bar_start(struct battle_bar *bar, struct battle *bt)
-{
-	assert(bar);
-	assert(bt);
-
-	if (bar->start)
-		bar->start(bar, bt);
-}
-
-void
-battle_bar_select(struct battle_bar *bar, struct battle *bt, const struct selection *sel)
-{
-	assert(bar);
-	assert(bt);
-	assert(sel);
-
-	if (bar->select)
-		bar->select(bar, bt, sel);
-
-}
-
-void
-battle_bar_handle(struct battle_bar *bar, struct battle *bt, const union event *ev)
-{
-	assert(bar);
-	assert(bt);
-	assert(ev);
-
-	if (bar->handle)
-		bar->handle(bar, bt, ev);
-}
-
-void
-battle_bar_update(struct battle_bar *bar, struct battle *bt, unsigned int ticks)
-{
-	assert(bar);
-	assert(bt);
-
-	if (bar->update)
-		bar->update(bar, bt, ticks);
-}
-
-void
-battle_bar_draw(const struct battle_bar *bar, const struct battle *bt)
-{
-	assert(bar);
-	assert(bt);
-
-	if (bar->draw)
-		bar->draw(bar, bt);
-}
-
-void
-battle_bar_finish(struct battle_bar *bar, struct battle *bt)
-{
-	assert(bar);
-	assert(bt);
-
-	if (bar->finish)
-		bar->finish(bar, bt);
-}
--- a/src/libmlk-rpg/rpg/battle-bar.h	Sat Oct 15 21:19:25 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,61 +0,0 @@
-/*
- * battle-bar.h -- abstract battle bar
- *
- * Copyright (c) 2020-2022 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 MLK_RPG_BATTLE_BAR_H
-#define MLK_RPG_BATTLE_BAR_H
-
-#include <mlk/core/core.h>
-
-struct battle;
-struct selection;
-
-union event;
-
-struct battle_bar {
-	void *data;
-	void (*start)(struct battle_bar *, struct battle *);
-	void (*select)(struct battle_bar *, struct battle *, const struct selection *);
-	void (*handle)(struct battle_bar *, struct battle *, const union event *);
-	void (*update)(struct battle_bar *, struct battle *, unsigned int);
-	void (*draw)(const struct battle_bar *, const struct battle *);
-	void (*finish)(struct battle_bar *, struct battle *);
-};
-
-CORE_BEGIN_DECLS
-
-void
-battle_bar_start(struct battle_bar *, struct battle *);
-
-void
-battle_bar_select(struct battle_bar *, struct battle *, const struct selection *);
-
-void
-battle_bar_handle(struct battle_bar *, struct battle *, const union event *);
-
-void
-battle_bar_update(struct battle_bar *, struct battle *, unsigned int);
-
-void
-battle_bar_draw(const struct battle_bar *, const struct battle *);
-
-void
-battle_bar_finish(struct battle_bar *, struct battle *);
-
-CORE_END_DECLS
-
-#endif /* !MLK_RPG_BATTLE_BAR_H */
--- a/src/libmlk-rpg/rpg/battle-entity-state-attacking.c	Sat Oct 15 21:19:25 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,101 +0,0 @@
-/*
- * battle-entity-state-attacking.h -- the entity is attacking
- *
- * Copyright (c) 2020-2022 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 <stdlib.h>
-
-#include <mlk/core/alloc.h>
-#include <mlk/core/animation.h>
-#include <mlk/core/panic.h>
-#include <mlk/core/sprite.h>
-
-#include "battle-entity-state-attacking.h"
-#include "battle-entity-state.h"
-#include "battle-entity.h"
-
-struct self {
-	struct battle_entity_state_attacking data;
-	struct battle_entity_state state;
-};
-
-static int
-update(struct battle_entity_state *st, struct battle_entity *et, unsigned int ticks)
-{
-	(void)et;
-
-	return battle_entity_state_attacking_update(st->data, ticks);
-}
-
-static void
-draw(const struct battle_entity_state *st, const struct battle_entity *et)
-{
-	battle_entity_state_attacking_draw(st->data, et);
-}
-
-static void
-finish(struct battle_entity_state *st, struct battle_entity *et)
-{
-	(void)et;
-
-	free(st->data);
-}
-
-void
-battle_entity_state_attacking_init(struct battle_entity_state_attacking *atk, const struct sprite *which)
-{
-	assert(atk);
-	assert(sprite_ok(which));
-
-	animation_init(&atk->anim, which, 100);
-	animation_start(&atk->anim);
-}
-
-int
-battle_entity_state_attacking_update(struct battle_entity_state_attacking *atk, unsigned int ticks)
-{
-	assert(atk);
-
-	return animation_update(&atk->anim, ticks);
-}
-
-void
-battle_entity_state_attacking_draw(const struct battle_entity_state_attacking *atk, const struct battle_entity *et)
-{
-	assert(atk);
-	assert(battle_entity_ok(et));
-
-	animation_draw(&atk->anim, et->x, et->y);
-}
-
-void
-battle_entity_state_attacking(struct battle_entity *et, const struct sprite *which)
-{
-	assert(battle_entity_ok(et));
-	assert(sprite_ok(which));
-
-	struct self *self;
-
-	self = alloc_new0(sizeof (*self));
-	self->state.data = self;
-	self->state.update = update;
-	self->state.draw = draw;
-	self->state.finish = finish;
-
-	battle_entity_state_attacking_init(&self->data, which);
-	battle_entity_switch(et, &self->state);
-}
--- a/src/libmlk-rpg/rpg/battle-entity-state-attacking.h	Sat Oct 15 21:19:25 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,43 +0,0 @@
-/*
- * battle-entity-state-attacking.c -- the entity is attacking
- *
- * Copyright (c) 2020-2022 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 MLK_RPG_BATTLE_ENTITY_STATE_ATTACKING_H
-#define MLK_RPG_BATTLE_ENTITY_STATE_ATTACKING_H
-
-#include <mlk/core/animation.h>
-
-struct battle_entity;
-struct sprite;
-
-struct battle_entity_state_attacking {
-	struct animation anim;
-};
-
-void
-battle_entity_state_attacking_init(struct battle_entity_state_attacking *, const struct sprite *);
-
-int
-battle_entity_state_attacking_update(struct battle_entity_state_attacking *, unsigned int);
-
-void
-battle_entity_state_attacking_draw(const struct battle_entity_state_attacking *, const struct battle_entity *);
-
-void
-battle_entity_state_attacking(struct battle_entity *, const struct sprite *);
-
-#endif /* !MLK_RPG_BATTLE_ENTITY_STATE_ATTACKING_H */
--- a/src/libmlk-rpg/rpg/battle-entity-state-blinking.c	Sat Oct 15 21:19:25 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,97 +0,0 @@
-/*
- * battle-entity-state-blinking.c -- the entity is blinking
- *
- * Copyright (c) 2020-2022 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 <stdlib.h>
-
-#include <mlk/core/alloc.h>
-#include <mlk/core/panic.h>
-#include <mlk/core/sprite.h>
-#include <mlk/core/texture.h>
-
-#include "battle-entity-state-blinking.h"
-#include "battle-entity-state.h"
-#include "battle-entity.h"
-#include "character.h"
-
-#define TRANSPARENT     (150)
-#define OPAQUE          (255)
-
-struct self {
-	struct battle_entity_state_blinking data;
-	struct battle_entity_state state;
-};
-
-static int
-update(struct battle_entity_state *st, struct battle_entity *et, unsigned int ticks)
-{
-	(void)et;
-
-	return battle_entity_state_blinking_update(st->data, ticks);
-}
-
-static void
-finish(struct battle_entity_state *st, struct battle_entity *et)
-{
-	(void)et;
-
-	free(st->data);
-}
-
-void
-battle_entity_state_blinking_init(struct battle_entity_state_blinking *blk, struct battle_entity *et)
-{
-	assert(blk);
-	assert(battle_entity_ok(et));
-
-	blk->tex = et->ch->sprites[CHARACTER_SPRITE_NORMAL]->texture;
-	texture_set_alpha_mod(blk->tex, TRANSPARENT);
-}
-
-int
-battle_entity_state_blinking_update(struct battle_entity_state_blinking *blk, unsigned int ticks)
-{
-	assert(blk);
-
-	blk->elapsed += ticks;
-
-	if (blk->elapsed >= 80) {
-		blk->count += 1;
-		blk->elapsed = 0;
-	}
-
-	texture_set_alpha_mod(blk->tex, blk->count % 2 == 0 ? TRANSPARENT : OPAQUE);
-
-	return blk->count >= 3;
-}
-
-void
-battle_entity_state_blinking(struct battle_entity *et)
-{
-	assert(battle_entity_ok(et));
-
-	struct self *self;
-
-	self = alloc_new0(sizeof (*self));
-	self->state.data = self;
-	self->state.update = update;
-	self->state.finish = finish;
-
-	battle_entity_state_blinking_init(&self->data, et);
-	battle_entity_switch(et, &self->state);
-}
--- a/src/libmlk-rpg/rpg/battle-entity-state-blinking.h	Sat Oct 15 21:19:25 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,40 +0,0 @@
-/*
- * battle-entity-state-blinking.h -- the entity is blinking
- *
- * Copyright (c) 2020-2022 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 MLK_RPG_BATTLE_ENTITY_STATE_BLINKING_H
-#define MLK_RPG_BATTLE_ENTITY_STATE_BLINKING_H
-
-struct battle_entity;
-struct texture;
-
-struct battle_entity_state_blinking {
-	struct texture *tex;
-	unsigned int elapsed;
-	unsigned int count;
-};
-
-void
-battle_entity_state_blinking_init(struct battle_entity_state_blinking *, struct battle_entity *et);
-
-int
-battle_entity_state_blinking_update(struct battle_entity_state_blinking *, unsigned int);
-
-void
-battle_entity_state_blinking(struct battle_entity *);
-
-#endif /* !MLK_RPG_BATTLE_ENTITY_STATE_BLINKING_H */
--- a/src/libmlk-rpg/rpg/battle-entity-state-moving.c	Sat Oct 15 21:19:25 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,129 +0,0 @@
-/*
- * battle-entity-state-moving.c -- the entity is moving
- *
- * Copyright (c) 2020-2022 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 <math.h>
-#include <stdlib.h>
-
-#include <mlk/core/alloc.h>
-#include <mlk/core/panic.h>
-
-#include "battle-entity-state-moving.h"
-#include "battle-entity-state.h"
-#include "battle-entity.h"
-#include "character.h"
-#include "walksprite.h"
-
-#define SPEED 800
-#define SEC   1000
-#define WALK  40
-
-struct self {
-	struct battle_entity_state_moving data;
-	struct battle_entity_state state;
-};
-
-static inline unsigned int
-orientation(const struct battle_entity_state_moving *mv, const struct battle_entity *et)
-{
-	/* TODO: support diagonal. */
-	/* See: walksprite definitions. */
-	return mv->x < et->x ? 6 : 2;
-}
-
-static int
-update(struct battle_entity_state *st, struct battle_entity *et, unsigned int ticks)
-{
-	return battle_entity_state_moving_update(st->data, et, ticks);
-}
-
-static void
-draw(const struct battle_entity_state *st, const struct battle_entity *et)
-{
-	battle_entity_state_moving_draw(st->data, et);
-}
-
-static void
-finish(struct battle_entity_state *st, struct battle_entity *et)
-{
-	(void)et;
-
-	free(st->data);
-}
-
-void
-battle_entity_state_moving_init(struct battle_entity_state_moving *mv, struct battle_entity *et, int dstx, int dsty)
-{
-	assert(mv);
-	assert(battle_entity_ok(et));
-
-	walksprite_init(&mv->ws, et->ch->sprites[CHARACTER_SPRITE_NORMAL], 40);
-	mv->x = dstx;
-	mv->y = dsty;
-}
-
-int
-battle_entity_state_moving_update(struct battle_entity_state_moving *mv, struct battle_entity *et, unsigned int ticks)
-{
-	assert(mv);
-	assert(battle_entity_ok(et));
-
-	int step_x, step_y, delta_x, delta_y;
-
-	delta_x = mv->x < et->x ? -1 : +1;
-	delta_y = mv->y < et->y ? -1 : +1;
-	step_x = fmin(SPEED * ticks / SEC, abs(et->x - mv->x));
-	step_y = fmin(SPEED * ticks / SEC, abs(et->y - mv->y));
-
-	et->x += delta_x * step_x;
-	et->y += delta_y * step_y;
-
-	if (et->x != mv->x || et->y != mv->y)
-		walksprite_update(&mv->ws, ticks);
-	else
-		walksprite_reset(&mv->ws);
-
-	return et->x == mv->x && et->y == mv->y;
-}
-
-void
-battle_entity_state_moving_draw(const struct battle_entity_state_moving *mv, const struct battle_entity *et)
-{
-	assert(mv);
-	assert(battle_entity_ok(et));
-
-	/* TODO: compute orientation. */
-	walksprite_draw(&mv->ws, orientation(mv, et), et->x, et->y);
-}
-
-void
-battle_entity_state_moving(struct battle_entity *et, int dstx, int dsty)
-{
-	assert(battle_entity_ok(et));
-
-	struct self *self;
-
-	self = alloc_new0(sizeof (*self));
-	self->state.data = self;
-	self->state.update = update;
-	self->state.draw = draw;
-	self->state.finish = finish;
-
-	battle_entity_state_moving_init(&self->data, et, dstx, dsty);
-	battle_entity_switch(et, &self->state);
-}
--- a/src/libmlk-rpg/rpg/battle-entity-state-moving.h	Sat Oct 15 21:19:25 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,44 +0,0 @@
-/*
- * battle-entity-state-moving.h -- the entity is moving
- *
- * Copyright (c) 2020-2022 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 MLK_RPG_BATTLE_ENTITY_STATE_MOVING_H
-#define MLK_RPG_BATTLE_ENTITY_STATE_MOVING_H
-
-#include <rpg/walksprite.h>
-
-struct battle_entity;
-
-struct battle_entity_state_moving {
-	struct walksprite ws;
-	int x;
-	int y;
-};
-
-void
-battle_entity_state_moving_init(struct battle_entity_state_moving *, struct battle_entity *, int, int);
-
-int
-battle_entity_state_moving_update(struct battle_entity_state_moving *, struct battle_entity *, unsigned int);
-
-void
-battle_entity_state_moving_draw(const struct battle_entity_state_moving *, const struct battle_entity *);
-
-void
-battle_entity_state_moving(struct battle_entity *, int, int);
-
-#endif /* !MLK_RPG_BATTLE_ENTITY_STATE_MOVING_H */
--- a/src/libmlk-rpg/rpg/battle-entity-state-normal.c	Sat Oct 15 21:19:25 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,36 +0,0 @@
-/*
- * battle-entity-state-normal.c -- the entity is normal
- *
- * Copyright (c) 2020-2022 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 "battle-entity-state-normal.h"
-#include "battle-entity-state.h"
-#include "battle-entity.h"
-
-/* TODO: animate characters when they are inactive. */
-
-void
-battle_entity_state_normal(struct battle_entity *et)
-{
-	assert(battle_entity_ok(et));
-
-	/* Not needed yet. */
-	static struct battle_entity_state st;
-
-	battle_entity_switch(et, &st);
-}
--- a/src/libmlk-rpg/rpg/battle-entity-state-normal.h	Sat Oct 15 21:19:25 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,28 +0,0 @@
-/*
- * battle-entity-state-normal.h -- the entity is normal
- *
- * Copyright (c) 2020-2022 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 MLK_RPG_BATTLE_ENTITY_STATE_NORMAL_H
-#define MLK_RPG_BATTLE_ENTITY_STATE_NORMAL_H
-
-struct battle_entity;
-
-void
-battle_entity_state_normal(struct battle_entity *);
-
-#endif /* !MLK_RPG_BATTLE_ENTITY_STATE_NORMAL_H */
-
--- a/src/libmlk-rpg/rpg/battle-entity-state.c	Sat Oct 15 21:19:25 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,55 +0,0 @@
-/*
- * battle-entity-state.c -- abstract battle entity state
- *
- * Copyright (c) 2020-2022 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 "battle-entity.h"
-#include "battle-entity-state.h"
-
-int
-battle_entity_state_update(struct battle_entity_state *st, struct battle_entity *et, unsigned int ticks)
-{
-	assert(st);
-	assert(battle_entity_ok(et));
-
-	if (st->update)
-		return st->update(st, et, ticks);
-
-	return 1;
-}
-
-void
-battle_entity_state_draw(const struct battle_entity_state *st, const struct battle_entity *et)
-{
-	assert(st);
-	assert(battle_entity_ok(et));
-
-	if (st->draw)
-		st->draw(st, et);
-	else
-		battle_entity_draw_sprite(et);
-}
-
-void
-battle_entity_state_finish(struct battle_entity_state *st, struct battle_entity *et)
-{
-	assert(battle_entity_ok(et));
-
-	if (st->finish)
-		st->finish(st, et);
-}
--- a/src/libmlk-rpg/rpg/battle-entity-state.h	Sat Oct 15 21:19:25 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,47 +0,0 @@
-/*
- * battle-entity-state.h -- abstract battle entity state
- *
- * Copyright (c) 2020-2022 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 MLK_RPG_BATTLE_ENTITY_STATE_H
-#define MLK_RPG_BATTLE_ENTITY_STATE_H
-
-#include <mlk/core/core.h>
-
-struct battle_entity;
-struct sprite;
-
-struct battle_entity_state {
-	void *data;
-	int (*update)(struct battle_entity_state *, struct battle_entity *, unsigned int);
-	void (*draw)(const struct battle_entity_state *, const struct battle_entity *);
-	void (*finish)(struct battle_entity_state *, struct battle_entity *);
-};
-
-CORE_BEGIN_DECLS
-
-int
-battle_entity_state_update(struct battle_entity_state *, struct battle_entity *, unsigned int);
-
-void
-battle_entity_state_draw(const struct battle_entity_state *, const struct battle_entity *);
-
-void
-battle_entity_state_finish(struct battle_entity_state *, struct battle_entity *);
-
-CORE_END_DECLS
-
-#endif /* !MLK_RPG_BATTLE_ENTITY_STATE_H */
--- a/src/libmlk-rpg/rpg/battle-entity.c	Sat Oct 15 21:19:25 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,119 +0,0 @@
-/*
- * battle-entity.c -- in game battle entity
- *
- * Copyright (c) 2020-2022 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 <mlk/core/sprite.h>
-#include <mlk/core/texture.h>
-
-#include <mlk/ui/theme.h>
-
-#include "battle.h"
-#include "battle-entity.h"
-#include "battle-entity-state.h"
-#include "battle-entity-state-normal.h"
-#include "character.h"
-
-static void
-draw_name(const struct battle_entity *et, const struct battle *bt)
-{
-	struct label label = et->name;
-
-	label.theme = BATTLE_THEME(bt);
-
-	if (et == battle_current(bt))
-		label.flags |=  LABEL_FLAGS_SELECTED;
-	else
-		label.flags &= ~LABEL_FLAGS_SELECTED;
-
-	label_draw(&label);
-}
-
-void
-battle_entity_init(struct battle_entity *et)
-{
-	assert(et);
-
-	character_reset(et->ch);
-	texture_set_alpha_mod(et->ch->sprites[CHARACTER_SPRITE_NORMAL]->texture, 255);
-
-	battle_entity_state_normal(et);
-}
-
-int
-battle_entity_ok(const struct battle_entity *et)
-{
-	return et && character_ok(et->ch);
-}
-
-void
-battle_entity_switch(struct battle_entity *et, struct battle_entity_state *st)
-{
-	assert(et);
-	assert(st);
-
-	if (et->state)
-		battle_entity_state_finish(et->state, et);
-
-	et->state = st;
-}
-
-int
-battle_entity_update(struct battle_entity *et, unsigned int ticks)
-{
-	assert(et);
-
-	return battle_entity_state_update(et->state, et, ticks);
-}
-
-void
-battle_entity_draw_sprite(const struct battle_entity *et)
-{
-	struct sprite *sprite = et->ch->sprites[CHARACTER_SPRITE_NORMAL];
-	int row;
-
-	/*
-	 * Ennemies are usually defined with a single image as such the
-	 * sprite may contain only one cell/row. Otherwise if the user
-	 * have provided a structured sprite, use appropriate row.
-	 */
-	if (sprite->nrows >= 6)
-		row = 6;
-	else
-		row = 0;
-
-	sprite_draw(sprite, row, 0, et->x, et->y);
-}
-
-void
-battle_entity_draw(const struct battle_entity *et, const struct battle *bt)
-{
-	assert(et);
-	assert(bt);
-
-	draw_name(et, bt);
-	battle_entity_state_draw(et->state, et);
-}
-
-void
-battle_entity_finish(struct battle_entity *et)
-{
-	assert(et);
-
-	battle_entity_state_finish(et->state, et);
-}
--- a/src/libmlk-rpg/rpg/battle-entity.h	Sat Oct 15 21:19:25 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,63 +0,0 @@
-/*
- * battle-entity.h -- in game battle entity
- *
- * Copyright (c) 2020-2022 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 MLK_RPG_BATTLE_ENTITY_H
-#define MLK_RPG_BATTLE_ENTITY_H
-
-#include <mlk/core/core.h>
-
-#include <mlk/ui/label.h>
-
-struct battle;
-struct battle_entity_state;
-struct character;
-
-struct battle_entity {
-	struct character *ch;
-	int x;
-	int y;
-	struct label name;
-	struct battle_entity_state *state;
-};
-
-CORE_BEGIN_DECLS
-
-void
-battle_entity_init(struct battle_entity *);
-
-int
-battle_entity_ok(const struct battle_entity *);
-
-void
-battle_entity_switch(struct battle_entity *, struct battle_entity_state *);
-
-int
-battle_entity_update(struct battle_entity *, unsigned int);
-
-void
-battle_entity_draw(const struct battle_entity *, const struct battle *);
-
-void
-battle_entity_draw_sprite(const struct battle_entity *);
-
-void
-battle_entity_finish(struct battle_entity *);
-
-CORE_END_DECLS
-
-#endif /* !MLK_RPG_BATTLE_ENTITY_H */
--- a/src/libmlk-rpg/rpg/battle-indicator.c	Sat Oct 15 21:19:25 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,134 +0,0 @@
-/*
- * battle-indicator.c -- drawable for rendering a hp/mp count usage
- *
- * Copyright (c) 2020-2022 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 <math.h>
-#include <stdio.h>
-#include <string.h>
-
-#include <mlk/core/color.h>
-#include <mlk/core/font.h>
-#include <mlk/core/panic.h>
-
-#include <mlk/ui/theme.h>
-
-#include "battle-indicator.h"
-
-#define THEME(bti)      ((bti)->theme ? (bti)->theme : theme_default())
-#define STEP            (2)
-#define DELAY           (5)
-
-static inline unsigned int
-inc(int cmp, int tgt)
-{
-	return tgt > cmp ? fmin(cmp + STEP, tgt) : fmax(cmp - STEP, tgt);
-}
-
-static inline int
-colored(const struct battle_indicator *bti)
-{
-	/* Only check r, g, b and ignore alpha. */
-	return COLOR_R(bti->cur) == COLOR_R(bti->color) &&
-	       COLOR_G(bti->cur) == COLOR_G(bti->color) &&
-	       COLOR_B(bti->cur) == COLOR_B(bti->color);
-}
-
-void
-battle_indicator_start(struct battle_indicator *bti)
-{
-	assert(bti);
-
-	char buf[128];
-	const struct theme *theme = THEME(bti);
-
-	snprintf(buf, sizeof (buf), "%u", bti->amount);
-
-	bti->cur = 0xffffffff;
-	bti->elapsed = 0;
-	bti->alpha = 250;
-
-	if (font_render(theme->fonts[THEME_FONT_INTERFACE], &bti->tex[0], buf, bti->cur) < 0||
-	    font_render(theme->fonts[THEME_FONT_INTERFACE], &bti->tex[1], buf, 0x000000ff) < 0)
-		panic();
-}
-
-int
-battle_indicator_completed(const struct battle_indicator *bti)
-{
-	assert(battle_indicator_ok(bti));
-
-	return colored(bti) && bti->alpha == 0;
-}
-
-int
-battle_indicator_ok(const struct battle_indicator *bti)
-{
-	return bti && texture_ok(&bti->tex[0]) && texture_ok(&bti->tex[1]);
-}
-
-int
-battle_indicator_update(struct battle_indicator *bti, unsigned int ticks)
-{
-	assert(battle_indicator_ok(bti));
-
-	bti->elapsed += ticks;
-
-	if (bti->elapsed > DELAY) {
-		bti->elapsed = 0;
-
-		if (!colored(bti)) {
-			/* Update colors first. */
-			bti->cur = COLOR_HEX(
-			    inc(COLOR_R(bti->cur), COLOR_R(bti->color)),
-			    inc(COLOR_G(bti->cur), COLOR_G(bti->color)),
-			    inc(COLOR_B(bti->cur), COLOR_B(bti->color)),
-			    255
-			);
-
-			texture_set_color_mod(&bti->tex[0], bti->cur);
-		} else {
-			/* Update alpha next. */
-			bti->alpha -= 10;
-
-			texture_set_alpha_mod(&bti->tex[0], bti->alpha);
-			texture_set_alpha_mod(&bti->tex[1], bti->alpha);
-		}
-	}
-
-	return battle_indicator_completed(bti);
-}
-
-void
-battle_indicator_draw(const struct battle_indicator *bti, int x, int y)
-{
-	assert(battle_indicator_ok(bti));
-
-	texture_draw(&bti->tex[1], x + 1, y + 1);
-	texture_draw(&bti->tex[0], x, y);
-}
-
-void
-battle_indicator_finish(struct battle_indicator *bti)
-{
-	assert(bti);
-
-	texture_finish(&bti->tex[0]);
-	texture_finish(&bti->tex[1]);
-
-	memset(bti, 0, sizeof (*bti));
-}
--- a/src/libmlk-rpg/rpg/battle-indicator.h	Sat Oct 15 21:19:25 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,62 +0,0 @@
-/*
- * battle-indicator.h -- drawable for rendering a hp/mp count usage
- *
- * Copyright (c) 2020-2022 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 MLK_RPG_BATTLE_INDICATOR_H
-#define MLK_RPG_BATTLE_INDICATOR_H
-
-#include <mlk/core/core.h>
-#include <mlk/core/texture.h>
-
-#define BATTLE_INDICATOR_HP_COLOR (0xa5303000)
-#define BATTLE_INDICATOR_MP_COLOR (0xa23e8c00)
-
-struct theme;
-
-struct battle_indicator {
-	unsigned int color;
-	unsigned int amount;
-	const struct theme *theme;
-	unsigned int cur;
-	unsigned int elapsed;
-	unsigned int alpha;
-	struct texture tex[2];
-};
-
-CORE_BEGIN_DECLS
-
-void
-battle_indicator_start(struct battle_indicator *);
-
-int
-battle_indicator_completed(const struct battle_indicator *);
-
-int
-battle_indicator_ok(const struct battle_indicator *);
-
-int
-battle_indicator_update(struct battle_indicator *, unsigned int);
-
-void
-battle_indicator_draw(const struct battle_indicator *, int, int);
-
-void
-battle_indicator_finish(struct battle_indicator *);
-
-CORE_END_DECLS
-
-#endif /* !MLK_RPG_BATTLE_INDICATOR_H */
--- a/src/libmlk-rpg/rpg/battle-message.c	Sat Oct 15 21:19:25 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,69 +0,0 @@
-/*
- * battle-message.c -- automatic top center message in battle
- *
- * Copyright (c) 2020-2022 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 <mlk/core/window.h>
-
-#include <mlk/ui/align.h>
-#include <mlk/ui/frame.h>
-#include <mlk/ui/label.h>
-
-#include "battle-message.h"
-
-#define DELAY 1500
-
-int
-battle_message_update(struct battle_message *msg, unsigned int ticks)
-{
-	assert(msg);
-
-	msg->elapsed += ticks;
-
-	return msg->elapsed >= DELAY;
-}
-
-void
-battle_message_draw(const struct battle_message *msg)
-{
-	assert(msg);
-
-	struct frame f = {0};
-	struct label l = {0};
-	unsigned int lw = 0, lh = 0;
-
-	/* Prepare message frame. */
-	f.w = window.w / 3;
-	f.h = window.h / 15;
-	f.theme = msg->theme;
-
-	/* Center on top. */
-	align(ALIGN_TOP, &f.x, &f.y, f.w, f.h, 0, 20, window.w, window.h);
-
-	/* Prepare message label box. */
-	l.text = msg->text;
-	l.flags = LABEL_FLAGS_SHADOW;
-	l.theme = msg->theme;
-	label_query(&l, &lw, &lh);
-
-	/* Align the text in the box. */
-	align(ALIGN_CENTER, &l.x, &l.y, lw, lh, f.x, f.y, f.w, f.h);
-
-	frame_draw(&f);
-	label_draw(&l);
-}
--- a/src/libmlk-rpg/rpg/battle-message.h	Sat Oct 15 21:19:25 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,42 +0,0 @@
-/*
- * battle-message.h -- automatic top center message in battle
- *
- * Copyright (c) 2020-2022 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 MLK_RPG_BATTLE_MESSAGE_H
-#define MLK_RPG_BATTLE_MESSAGE_H
-
-#include <mlk/core/core.h>
-
-struct theme;
-
-struct battle_message {
-	const char *text;
-	struct theme *theme;
-	unsigned int elapsed;
-};
-
-CORE_BEGIN_DECLS
-
-int
-battle_message_update(struct battle_message *, unsigned int);
-
-void
-battle_message_draw(const struct battle_message *);
-
-CORE_END_DECLS
-
-#endif /* !MLK_RPG_BATTLE_MESSAGE_H */
--- a/src/libmlk-rpg/rpg/battle-state-ai.c	Sat Oct 15 21:19:25 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,92 +0,0 @@
-/*
- * battle-state-ai.c -- battle state (enemy is playing)
- *
- * Copyright (c) 2020-2022 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 <stdlib.h>
-
-#include <mlk/core/alloc.h>
-
-#include "battle-state-ai.h"
-#include "battle-state.h"
-#include "battle.h"
-#include "character.h"
-
-static int
-update(struct battle_state *st, struct battle *bt, unsigned int ticks)
-{
-	(void)st;
-	(void)ticks;
-
-	battle_state_ai_update(bt);
-
-	return 0;
-}
-
-static void
-draw(const struct battle_state *st, const struct battle *bt)
-{
-	(void)st;
-
-	battle_state_ai_draw(bt);
-}
-
-static void
-finish(struct battle_state *st, struct battle *bt)
-{
-	(void)bt;
-
-	free(st);
-}
-
-void
-battle_state_ai_update(struct battle *bt)
-{
-	assert(battle_ok(bt));
-
-	struct character *ch = battle_current(bt)->ch;
-
-	/*
-	 * Immediately invoke the enemy exec strategy and put the battle state
-	 * to check.
-	 */
-	character_exec(ch, bt);
-}
-
-void
-battle_state_ai_draw(const struct battle *bt)
-{
-	assert(battle_ok(bt));
-
-	battle_draw_component(bt, BATTLE_COMPONENT_ALL);
-}
-
-void
-battle_state_ai(struct battle *bt)
-{
-	assert(bt);
-
-	struct battle_state *self;
-
-	self = alloc_new0(sizeof (*self));
-	self->data = bt;
-	self->update = update;
-	self->draw = draw;
-	self->finish = finish;
-
-	battle_switch(bt, self);
-}
--- a/src/libmlk-rpg/rpg/battle-state-ai.h	Sat Oct 15 21:19:25 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,33 +0,0 @@
-/*
- * battle-state-ai.h -- battle state (enemy is playing)
- *
- * Copyright (c) 2020-2022 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 MLK_RPG_BATTLE_STATE_AI_H
-#define MLK_RPG_BATTLE_STATE_AI_H
-
-struct battle;
-
-void
-battle_state_ai_update(struct battle *);
-
-void
-battle_state_ai_draw(const struct battle *);
-
-void
-battle_state_ai(struct battle *);
-
-#endif /* !MLK_RPG_BATTLE_STATE_AI_H */
--- a/src/libmlk-rpg/rpg/battle-state-attacking.c	Sat Oct 15 21:19:25 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,176 +0,0 @@
-/*
- * battle-state-attacking.c -- battle state (entity is moving for attacking)
- *
- * Copyright (c) 2020-2022 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 <stdlib.h>
-
-#include <mlk/core/alloc.h>
-#include <mlk/core/panic.h>
-#include <mlk/core/sprite.h>
-
-#include "battle-entity-state-attacking.h"
-#include "battle-entity-state-blinking.h"
-#include "battle-entity-state-moving.h"
-#include "battle-entity-state-normal.h"
-#include "battle-entity-state.h"
-#include "battle-state-attacking.h"
-#include "battle-state-check.h"
-#include "battle-state.h"
-#include "battle.h"
-#include "character.h"
-
-struct self {
-	struct battle_state_attacking data;
-	struct battle_state state;
-};
-
-static void
-damage(const struct battle_entity *source, struct battle_entity *target, struct battle *bt)
-{
-	(void)source;
-
-	/* TODO: math calculation here.*/
-	target->ch->hp -= 50;
-
-	if (target->ch->hp < 0)
-		target->ch->hp = 0;
-
-	battle_indicator_hp(bt, target->ch, 50);
-}
-
-static int
-update(struct battle_state *st, struct battle *bt, unsigned int ticks)
-{
-	battle_state_attacking_update(st->data, bt, ticks);
-
-	return 0;
-}
-
-static void
-draw(const struct battle_state *st, const struct battle *bt)
-{
-	battle_state_attacking_draw(st->data, bt);
-}
-
-static void
-finish(struct battle_state *st, struct battle *bt)
-{
-	(void)bt;
-
-	free(st->data);
-}
-
-void
-battle_state_attacking_init(struct battle_state_attacking *atk,
-                            struct battle_entity *source,
-                            struct battle_entity *target)
-{
-	assert(atk);
-	assert(battle_entity_ok(source));
-	assert(battle_entity_ok(target));
-
-	int x, y;
-
-	/* Starts this state with advancing. */
-	atk->source = source;
-	atk->target = target;
-	atk->origin_x = source->x;
-	atk->origin_y = source->y;
-
-	/* We go to the enemy. */
-	x = target->x + target->ch->sprites[CHARACTER_SPRITE_NORMAL]->cellw;
-	y = target->y;
-
-	/* If it is an enemy we don't move it but blink instead. */
-	if (source->ch->exec) {
-		atk->status = BATTLE_STATE_ATTACKING_BLINKING;
-		battle_entity_state_blinking(source);
-	} else
-		battle_entity_state_moving(source, x, y);
-}
-
-void
-battle_state_attacking_update(struct battle_state_attacking *atk, struct battle *bt, unsigned int ticks)
-{
-	assert(atk);
-	assert(bt);
-
-	battle_update_component(bt, ticks, BATTLE_COMPONENT_ALL);
-
-	if (!battle_entity_update(atk->source, 0))
-		return;
-
-	switch (atk->status) {
-	case BATTLE_STATE_ATTACKING_ADVANCING:
-		/*
-		 * Current entity state is battle-entity-state-moving but it is
-		 * already updated from the game itself so pass 0 just to check
-		 * if it has finished moving.
-		 */
-		atk->status = BATTLE_STATE_ATTACKING_DAMAGING;
-
-		/* TODO: determine sprite to use about equipment. */
-		battle_entity_state_attacking(atk->source, atk->source->ch->sprites[CHARACTER_SPRITE_SWORD]);
-		break;
-	case BATTLE_STATE_ATTACKING_DAMAGING:
-		/* Move back to original position. */
-		atk->status = BATTLE_STATE_ATTACKING_RETURNING;
-		damage(atk->source, atk->target, bt);
-		battle_entity_state_moving(atk->source, atk->origin_x, atk->origin_y);
-		break;
-	case BATTLE_STATE_ATTACKING_RETURNING:
-	case BATTLE_STATE_ATTACKING_BLINKING:
-		/* Just wait. */
-		battle_entity_state_normal(atk->source);
-		damage(atk->source, atk->target, bt);
-		battle_state_check(bt);
-		break;
-	default:
-		break;
-	}
-}
-
-void
-battle_state_attacking_draw(const struct battle_state_attacking *atk, const struct battle *bt)
-{
-	assert(atk);
-	assert(battle_ok(bt));
-
-	(void)atk;
-
-	battle_draw_component(bt, BATTLE_COMPONENT_ALL);
-}
-
-void
-battle_state_attacking(struct battle_entity *source, struct battle_entity *target, struct battle *bt)
-{
-	assert(battle_entity_ok(source));
-	assert(battle_entity_ok(target));
-	assert(bt);
-
-	struct self *self;
-
-	self = alloc_new0(sizeof (*self));
-	self->state.data = self;
-	self->state.update = update;
-	self->state.draw = draw;
-	self->state.finish = finish;
-
-	battle_state_attacking_init(&self->data, source, target);
-	battle_switch(bt, &self->state);
-}
--- a/src/libmlk-rpg/rpg/battle-state-attacking.h	Sat Oct 15 21:19:25 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,57 +0,0 @@
-/*
- * battle-state-attacking.h -- battle state (entity is moving for attacking)
- *
- * Copyright (c) 2020-2022 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 MLK_RPG_BATTLE_STATE_ATTACKING_H
-#define MLK_RPG_BATTLE_STATE_ATTACKING_H
-
-struct battle;
-struct battle_entity;
-
-enum battle_state_attacking_status {
-	/* For team. */
-	BATTLE_STATE_ATTACKING_ADVANCING,
-	BATTLE_STATE_ATTACKING_DAMAGING,
-	BATTLE_STATE_ATTACKING_RETURNING,
-
-	/* For enemies. */
-	BATTLE_STATE_ATTACKING_BLINKING,
-};
-
-struct battle_state_attacking {
-	enum battle_state_attacking_status status;
-	struct battle_entity *source;
-	struct battle_entity *target;
-	int origin_x;
-	int origin_y;
-};
-
-void
-battle_state_attacking_init(struct battle_state_attacking *,
-                            struct battle_entity *,
-                            struct battle_entity *);
-
-void
-battle_state_attacking_update(struct battle_state_attacking *, struct battle *, unsigned int);
-
-void
-battle_state_attacking_draw(const struct battle_state_attacking *, const struct battle *);
-
-void
-battle_state_attacking(struct battle_entity *, struct battle_entity *, struct battle *);
-
-#endif /* !#define MLK_RPG_BATTLE_STATE_ATTACKING_H */
--- a/src/libmlk-rpg/rpg/battle-state-check.c	Sat Oct 15 21:19:25 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,207 +0,0 @@
-/*
- * battle-state-check.c -- battle state (check status)
- *
- * Copyright (c) 2020-2022 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 <stdlib.h>
-#include <string.h>
-
-#include <mlk/core/alloc.h>
-#include <mlk/core/panic.h>
-#include <mlk/core/sprite.h>
-#include <mlk/core/texture.h>
-#include <mlk/core/trace.h>
-
-#include "battle-state-check.h"
-#include "battle-state-lost.h"
-#include "battle-state-victory.h"
-#include "battle-state.h"
-#include "battle.h"
-#include "character.h"
-
-struct fadeout {
-	struct character *ch;
-	int x;
-	int y;
-	struct drawable dw;
-	unsigned int alpha;
-	unsigned int elapsed;
-};
-
-static int
-fadeout_update(struct drawable *dw, unsigned int ticks)
-{
-	struct fadeout *fade = dw->data;
-
-	fade->elapsed += ticks;
-
-	if (fade->elapsed >= 8) {
-		fade->elapsed = 0;
-
-		if (fade->alpha == 0)
-			return 1;
-
-		fade->alpha -= 10;
-	}
-
-	return 0;
-}
-
-static void
-fadeout_draw(struct drawable *dw)
-{
-	const struct fadeout *fade = dw->data;
-	struct sprite *sprite = fade->ch->sprites[CHARACTER_SPRITE_NORMAL];
-
-	texture_set_alpha_mod(sprite->texture, fade->alpha);
-	sprite_draw(sprite, 0, 0, fade->x, fade->y);
-	texture_set_alpha_mod(sprite->texture, 255);
-}
-
-static void
-fadeout_finish(struct drawable *dw)
-{
-	free(dw->data);
-}
-
-static void
-fadeout(struct battle *bt, struct battle_entity *et)
-{
-	struct fadeout *fade;
-
-	if (!bt->effects) {
-		tracef("can't create a fadeout effect without a drawable_stack");
-		return;
-	}
-
-	fade = alloc_new0(sizeof (*fade));
-	fade->ch = et->ch;
-	fade->x = et->x;
-	fade->y = et->y;
-	fade->alpha = 250;
-	fade->dw.data = fade;
-	fade->dw.draw = fadeout_draw;
-	fade->dw.update = fadeout_update;
-	fade->dw.finish = fadeout_finish;
-
-	if (drawable_stack_add(bt->effects, &fade->dw) < 0)
-		free(fade);
-}
-
-static int
-is_dead(const struct battle *bt)
-{
-	const struct battle_entity *et;
-
-	BATTLE_TEAM_FOREACH(bt, et) {
-		if (!character_ok(et->ch))
-			continue;
-		if (et->ch->hp > 0)
-			return 0;
-	}
-
-	return 1;
-}
-
-static int
-is_won(const struct battle *bt)
-{
-	const struct battle_entity *et;
-
-	BATTLE_ENEMY_FOREACH(bt, et)
-		if (character_ok(et->ch))
-			return 0;
-
-	return 1;
-}
-
-static void
-clean(struct battle *bt)
-{
-	for (size_t i = 0; i < bt->enemiesz; ++i) {
-		if (bt->enemies[i] && character_ok(bt->enemies[i]->ch) && bt->enemies[i]->ch->hp == 0) {
-			fadeout(bt, bt->enemies[i]);
-			bt->enemies[i] = NULL;
-		}
-	}
-}
-
-static int
-update(struct battle_state *st, struct battle *bt, unsigned int ticks)
-{
-	(void)st;
-	(void)ticks;
-
-	battle_state_check_update(bt);
-
-	return 0;
-}
-
-static void
-draw(const struct battle_state *st, const struct battle *bt)
-{
-	(void)st;
-
-	battle_state_check_draw(bt);
-}
-
-static void
-finish(struct battle_state *st, struct battle *bt)
-{
-	(void)bt;
-
-	free(st);
-}
-
-void
-battle_state_check_update(struct battle *bt)
-{
-	assert(battle_ok(bt));
-
-	clean(bt);
-
-	if (is_dead(bt))
-		battle_state_lost(bt);
-	else if (is_won(bt))
-		battle_state_victory(bt);
-	else
-		battle_next(bt);
-}
-
-void
-battle_state_check_draw(const struct battle *bt)
-{
-	assert(battle_ok(bt));
-
-	battle_draw_component(bt, BATTLE_COMPONENT_ALL);
-}
-
-void
-battle_state_check(struct battle *bt)
-{
-	assert(battle_ok(bt));
-
-	struct battle_state *self;
-
-	self = alloc_new0(sizeof (*self));
-	self->data = bt;
-	self->update = update;
-	self->draw = draw;
-	self->finish = finish;
-
-	battle_switch(bt, self);
-}
--- a/src/libmlk-rpg/rpg/battle-state-check.h	Sat Oct 15 21:19:25 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,33 +0,0 @@
-/*
- * battle-state-check.h -- battle state (check status)
- *
- * Copyright (c) 2020-2022 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 MLK_RPG_BATTLE_STATE_CHECK_H
-#define MLK_RPG_BATTLE_STATE_CHECK_H
-
-struct battle;
-
-void
-battle_state_check_update(struct battle *);
-
-void
-battle_state_check_draw(const struct battle *);
-
-void
-battle_state_check(struct battle *);
-
-#endif /* !MLK_RPG_BATTLE_STATE_CHECK_H */
--- a/src/libmlk-rpg/rpg/battle-state-closing.c	Sat Oct 15 21:19:25 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,140 +0,0 @@
-/*
- * battle-state-closing.c -- battle state (closing)
- *
- * Copyright (c) 2020-2022 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 <stdlib.h>
-#include <string.h>
-
-#include <mlk/core/alloc.h>
-#include <mlk/core/music.h>
-#include <mlk/core/painter.h>
-#include <mlk/core/panic.h>
-#include <mlk/core/texture.h>
-#include <mlk/core/window.h>
-
-#include "battle-state-closing.h"
-#include "battle.h"
-
-struct self {
-	struct battle_state_closing data;
-	struct battle_state state;
-};
-
-static int
-update(struct battle_state *st, struct battle *bt, unsigned int ticks)
-{
-	(void)bt;
-
-	return battle_state_closing_update(st->data, ticks);
-}
-
-static void
-draw(const struct battle_state *st, const struct battle *bt)
-{
-	(void)bt;
-
-	battle_state_closing_draw(st->data, bt);
-}
-
-static void
-finish(struct battle_state *st, struct battle *bt)
-{
-	(void)bt;
-
-	battle_state_closing_finish(st->data);
-	free(st->data);
-}
-
-void
-battle_state_closing_init(struct battle_state_closing *cls)
-{
-	assert(cls);
-
-	if (texture_new(&cls->texture, window.w, window.h) < 0)
-		panic();
-
-	PAINTER_BEGIN(&cls->texture);
-	texture_set_blend_mode(&cls->texture, TEXTURE_BLEND_BLEND);
-	painter_set_color(0x000000ff);
-	painter_clear();
-	painter_draw_rectangle(0, 0, window.w, window.h);
-	PAINTER_END();
-
-	texture_set_alpha_mod(&cls->texture, 0);
-}
-
-int
-battle_state_closing_update(struct battle_state_closing *cls, unsigned int ticks)
-{
-	assert(cls);
-
-	cls->elapsed += ticks;
-
-	/* TODO: ??? */
-	if (cls->elapsed > 8) {
-		cls->elapsed = 0;
-
-		if (cls->alpha == 255) {
-			/* TODO: since OpenAL, no notion of global music. */
-#if 0
-			music_stop(0);
-#endif
-			return 1;
-		}
-
-		cls->alpha += 5;
-		texture_set_alpha_mod(&cls->texture, cls->alpha);
-	}
-
-	return 0;
-}
-
-void
-battle_state_closing_draw(const struct battle_state_closing *cls, const struct battle *bt)
-{
-	assert(cls);
-
-	battle_draw_component(bt, BATTLE_COMPONENT_ALL);
-	texture_draw(&cls->texture, 0, 0);
-}
-
-void
-battle_state_closing_finish(struct battle_state_closing *cls)
-{
-	assert(cls);
-
-	texture_finish(&cls->texture);
-	memset(cls, 0, sizeof (*cls));
-}
-
-void
-battle_state_closing(struct battle *bt)
-{
-	assert(bt);
-
-	struct self *self;
-
-	self = alloc_new0(sizeof (*self));
-	self->state.data = self;
-	self->state.update = update;
-	self->state.draw = draw;
-	self->state.finish = finish;
-
-	battle_state_closing_init(&self->data);
-	battle_switch(bt, &self->state);
-}
--- a/src/libmlk-rpg/rpg/battle-state-closing.h	Sat Oct 15 21:19:25 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,47 +0,0 @@
-/*
- * battle-state-closing.h -- battle state (closing)
- *
- * Copyright (c) 2020-2022 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 MLK_RPG_BATTLE_STATE_CLOSING_H
-#define MLK_RPG_BATTLE_STATE_CLOSING_H
-
-#include <mlk/core/texture.h>
-
-struct battle;
-
-struct battle_state_closing {
-	struct texture texture;
-	unsigned int alpha;
-	unsigned int elapsed;
-};
-
-void
-battle_state_closing_init(struct battle_state_closing *);
-
-int
-battle_state_closing_update(struct battle_state_closing *, unsigned int);
-
-void
-battle_state_closing_draw(const struct battle_state_closing *, const struct battle *);
-
-void
-battle_state_closing_finish(struct battle_state_closing *);
-
-void
-battle_state_closing(struct battle *);
-
-#endif /* !MLK_RPG_BATTLE_STATE_CLOSING_H */
--- a/src/libmlk-rpg/rpg/battle-state-item.c	Sat Oct 15 21:19:25 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,148 +0,0 @@
-/*
- * battle-state-item.c -- battle state (using item)
- *
- * Copyright (c) 2020-2022 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 <stdlib.h>
-
-#include <mlk/core/alloc.h>
-#include <mlk/core/panic.h>
-
-#include <rpg/inventory.h>
-#include <rpg/item.h>
-
-#include "battle-entity-state-moving.h"
-#include "battle-entity-state-normal.h"
-#include "battle-entity-state.h"
-#include "battle-message.h"
-#include "battle-state-item.h"
-#include "battle-state.h"
-#include "battle.h"
-
-struct self {
-	struct battle_state_item data;
-	struct battle_state state;
-};
-
-static int
-update(struct battle_state *st, struct battle *bt, unsigned int ticks)
-{
-	return battle_state_item_update(st->data, bt, ticks);
-}
-
-static void
-draw(const struct battle_state *st, const struct battle *bt)
-{
-	(void)bt;
-
-	battle_state_item_draw(st->data);
-}
-
-static void
-finish(struct battle_state *st, struct battle *bt)
-{
-	(void)bt;
-
-	free(st->data);
-}
-
-void
-battle_state_item_init(struct battle_state_item *it,
-                       struct battle *bt,
-                       struct battle_entity *source,
-                       struct battle_entity *target,
-                       struct inventory_slot *slot)
-{
-	assert(it);
-	assert(bt);
-	assert(battle_entity_ok(source));
-	assert(battle_entity_ok(target));
-	assert(slot);
-
-	it->source = source;
-	it->target = target;
-	it->slot = slot;
-	it->origin_x = it->source->x;
-
-	it->msg.text = slot->item->name;
-	it->msg.theme = bt->theme;
-
-	battle_entity_state_moving(it->source, it->origin_x - 100, it->source->y);
-}
-
-int
-battle_state_item_update(struct battle_state_item *it, struct battle *bt, unsigned int ticks)
-{
-	assert(it);
-	assert(bt);
-
-	switch (it->status) {
-	case BATTLE_STATE_ITEM_ADVANCING:
-		/* Entity is updating from battle, so just inspect its status. */
-		if (battle_entity_update(it->source, 0)) {
-			it->status = BATTLE_STATE_ITEM_MESSAGE;
-			battle_entity_state_normal(it->source);
-		}
-		break;
-	case BATTLE_STATE_ITEM_MESSAGE:
-		if (battle_message_update(&it->msg, ticks)) {
-			it->status = BATTLE_STATE_ITEM_RETURNING;
-			battle_entity_state_moving(it->source, it->origin_x, it->source->y);
-		}
-		break;
-	default:
-		if (battle_entity_update(it->source, 0)) {
-			battle_entity_state_normal(it->source);
-			battle_use(bt, it->slot->item, it->source->ch, it->target->ch);
-		}
-		break;
-	}
-
-	return 0;
-}
-
-void
-battle_state_item_draw(struct battle_state_item *it)
-{
-	assert(it);
-
-	if (it->status == BATTLE_STATE_ITEM_MESSAGE)
-		battle_message_draw(&it->msg);
-}
-
-void
-battle_state_item(struct battle *bt,
-                  struct battle_entity *source,
-                  struct battle_entity *target,
-                  struct inventory_slot *slot)
-{
-	assert(battle_entity_ok(source));
-	assert(battle_entity_ok(target));
-	assert(slot);
-	assert(bt);
-
-	struct self *self;
-
-	self = alloc_new0(sizeof (*self));
-	self->state.data = self;
-	self->state.update = update;
-	self->state.draw = draw;
-	self->state.finish = finish;
-
-	battle_state_item_init(&self->data, bt, source, target, slot);
-	battle_switch(bt, &self->state);
-}
--- a/src/libmlk-rpg/rpg/battle-state-item.h	Sat Oct 15 21:19:25 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,63 +0,0 @@
-/*
- * battle-state-item.h -- battle state (using item)
- *
- * Copyright (c) 2020-2022 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 MLK_RPG_BATTLE_STATE_ITEM_H
-#define MLK_RPG_BATTLE_STATE_ITEM_H
-
-#include <rpg/battle-message.h>
-
-struct battle;
-struct battle_entity;
-struct inventory_slot;
-
-enum battle_state_item_status {
-	BATTLE_STATE_ITEM_ADVANCING,
-	BATTLE_STATE_ITEM_MESSAGE,
-	BATTLE_STATE_ITEM_RETURNING
-};
-
-struct battle_state_item {
-	enum battle_state_item_status status;
-	struct battle_message msg;
-	struct battle_entity *source;
-	struct battle_entity *target;
-	struct inventory_slot *slot;
-	int origin_x;
-};
-
-void
-battle_state_item_init(struct battle_state_item *,
-                       struct battle *,
-                       struct battle_entity *,
-                       struct battle_entity *,
-                       struct inventory_slot *);
-
-int
-battle_state_item_update(struct battle_state_item *, struct battle *, unsigned int);
-
-void
-battle_state_item_draw(struct battle_state_item *);
-
-void
-battle_state_item(struct battle *,
-                  struct battle_entity *,
-                  struct battle_entity *,
-                  struct inventory_slot *);
-
-
-#endif /* !MLK_RPG_BATTLE_STATE_ITEM_H */
--- a/src/libmlk-rpg/rpg/battle-state-lost.c	Sat Oct 15 21:19:25 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,134 +0,0 @@
-/*
- * battle-state-lost.c -- battle state (lost)
- *
- * Copyright (c) 2020-2022 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 <mlk/core/alloc.h>
-#include <mlk/core/music.h>
-#include <mlk/core/panic.h>
-#include <mlk/core/window.h>
-
-#include "battle-state-closing.h"
-#include "battle-state-lost.h"
-#include "battle-state.h"
-#include "battle.h"
-
-struct self {
-	struct battle_state_lost data;
-	struct battle_state state;
-};
-
-static void
-handle(struct battle_state *st, struct battle *bt, const union event *ev)
-{
-	(void)bt;
-
-	battle_state_lost_handle(st->data, ev);
-}
-
-static int
-update(struct battle_state *st, struct battle *bt, unsigned int ticks)
-{
-	return battle_state_lost_update(st->data, bt, ticks);
-}
-
-static void
-draw(const struct battle_state *st, const struct battle *bt)
-{
-	(void)bt;
-
-	battle_state_lost_draw(st->data, bt);
-}
-
-void
-battle_state_lost_init(struct battle_state_lost *lost, struct battle *bt)
-{
-	assert(lost);
-	assert(bt);
-
-	lost->text = "You have been defeated...";
-
-	lost->msg.lines = &lost->text;
-	lost->msg.linesz = 1;
-	lost->msg.theme = bt->theme;
-	lost->msg.flags = MESSAGE_FLAGS_AUTOMATIC |
-	                  MESSAGE_FLAGS_FADEIN |
-	                  MESSAGE_FLAGS_FADEOUT;
-	lost->msg.timeout = MESSAGE_TIMEOUT_DEFAULT;
-	lost->msg.delay = MESSAGE_DELAY_DEFAULT;
-
-	message_start(&lost->msg);
-	message_query(&lost->msg, NULL, &lost->msg.h);
-
-	lost->msg.w = window.w * 0.6;
-	lost->msg.y = window.h * 0.1;
-	lost->msg.x = (window.w - lost->msg.w) / 2;
-
-	bt->status = BATTLE_STATUS_LOST;
-
-	if (bt->music[2])
-		music_play(bt->music[2], MUSIC_NONE);
-}
-
-void
-battle_state_lost_handle(struct battle_state_lost *lost, const union event *ev)
-{
-	assert(lost);
-	assert(ev);
-
-	message_handle(&lost->msg, ev);
-}
-
-int
-battle_state_lost_update(struct battle_state_lost *lost, struct battle *bt, unsigned int ticks)
-{
-	assert(lost);
-	assert(bt);
-
-	if (message_update(&lost->msg, ticks))
-		battle_state_closing(bt);
-
-	return 0;
-}
-
-void
-battle_state_lost_draw(struct battle_state_lost *lost, const struct battle *bt)
-{
-	assert(lost);
-	assert(battle_ok(bt));
-
-	battle_draw_component(bt, BATTLE_COMPONENT_ALL);
-	message_draw(&lost->msg);
-}
-
-void
-battle_state_lost(struct battle *bt)
-{
-	assert(bt);
-
-	struct self *self;
-
-	self = alloc_new0(sizeof (*self));
-	self->state.data = self;
-	self->state.handle = handle;
-	self->state.update = update;
-	self->state.draw = draw;
-
-	battle_state_lost_init(&self->data, bt);
-	battle_switch(bt, &self->state);
-}
--- a/src/libmlk-rpg/rpg/battle-state-lost.h	Sat Oct 15 21:19:25 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,48 +0,0 @@
-/*
- * battle-state-lost.h -- battle state (lost)
- *
- * Copyright (c) 2020-2022 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 MLK_RPG_BATTLE_STATE_LOST_H
-#define MLK_RPG_BATTLE_STATE_LOST_H
-
-#include <rpg/message.h>
-
-union event;
-
-struct battle;
-
-struct battle_state_lost {
-	const char *text;
-	struct message msg;
-};
-
-void
-battle_state_lost_init(struct battle_state_lost *, struct battle *);
-
-void
-battle_state_lost_handle(struct battle_state_lost *, const union event *);
-
-int
-battle_state_lost_update(struct battle_state_lost *, struct battle *, unsigned int);
-
-void
-battle_state_lost_draw(struct battle_state_lost *, const struct battle *);
-
-void
-battle_state_lost(struct battle *);
-
-#endif /* !MLK_RPG_BATTLE_STATE_LOST_H */
--- a/src/libmlk-rpg/rpg/battle-state-menu.c	Sat Oct 15 21:19:25 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,104 +0,0 @@
-/*
- * battle-state-menu.h -- battle state (menu)
- *
- * Copyright (c) 2020-2022 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 <stdlib.h>
-
-#include <mlk/core/alloc.h>
-
-#include "battle-bar.h"
-#include "battle-state-menu.h"
-#include "battle-state.h"
-#include "battle.h"
-
-static void
-handle(struct battle_state *st, struct battle *bt, const union event *ev)
-{
-	(void)st;
-
-	battle_state_menu_handle(bt, ev);
-}
-
-static int
-update(struct battle_state *st, struct battle *bt, unsigned int ticks)
-{
-	(void)st;
-
-	battle_state_menu_update(bt, ticks);
-
-	return 0;
-}
-
-static void
-draw(const struct battle_state *st, const struct battle *bt)
-{
-	(void)st;
-
-	battle_state_menu_draw(bt);
-}
-
-static void
-finish(struct battle_state *st, struct battle *bt)
-{
-	(void)bt;
-
-	free(st);
-}
-
-void
-battle_state_menu_handle(struct battle *bt, const union event *ev)
-{
-	assert(bt);
-	assert(ev);
-
-	battle_bar_handle(bt->bar, bt, ev);
-}
-
-void
-battle_state_menu_update(struct battle *bt, unsigned int ticks)
-{
-	assert(battle_ok(bt));
-
-	battle_update_component(bt, ticks, BATTLE_COMPONENT_ALL);
-}
-
-void
-battle_state_menu_draw(const struct battle *bt)
-{
-	assert(battle_ok(bt));
-
-	battle_draw_component(bt, BATTLE_COMPONENT_ALL);
-}
-
-void
-battle_state_menu(struct battle *bt)
-{
-	assert(bt);
-
-	struct battle_state *state;
-
-	state = alloc_new0(sizeof (*state));
-	state->data = bt;
-	state->handle = handle;
-	state->update = update;
-	state->draw = draw;
-	state->finish = finish;
-
-	battle_bar_start(bt->bar, bt);
-	battle_switch(bt, state);
-}
--- a/src/libmlk-rpg/rpg/battle-state-menu.h	Sat Oct 15 21:19:25 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,38 +0,0 @@
-/*
- * battle-state-menu.c -- battle state (menu)
- *
- * Copyright (c) 2020-2022 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 MLK_RPG_BATTLE_STATE_MENU_H
-#define MLK_RPG_BATTLE_STATE_MENU_H
-
-struct battle;
-
-union event;
-
-void
-battle_state_menu_handle(struct battle *, const union event *);
-
-void
-battle_state_menu_update(struct battle *, unsigned int);
-
-void
-battle_state_menu_draw(const struct battle *);
-
-void
-battle_state_menu(struct battle *);
-
-#endif /* !MLK_RPG_BATTLE_STATE_MENU_H */
--- a/src/libmlk-rpg/rpg/battle-state-opening.c	Sat Oct 15 21:19:25 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,111 +0,0 @@
-/*
- * battle-state-opening.c -- battle state (opening)
- *
- * Copyright (c) 2020-2022 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 <stdlib.h>
-
-#include <mlk/core/alloc.h>
-#include <mlk/core/painter.h>
-#include <mlk/core/panic.h>
-#include <mlk/core/window.h>
-
-#include "battle-state-check.h"
-#include "battle-state-opening.h"
-#include "battle-state.h"
-#include "battle.h"
-
-#define DELAY (1000U)
-
-struct self {
-	/* Always keep first. */
-	struct battle_state_opening data;
-	struct battle_state state;
-};
-
-static int
-update(struct battle_state *st, struct battle *bt, unsigned int ticks)
-{
-	(void)bt;
-
-	return battle_state_opening_update(st->data, bt, ticks);
-}
-
-static void
-draw(const struct battle_state *st, const struct battle *bt)
-{
-	(void)bt;
-
-	battle_state_opening_draw(st->data, bt);
-}
-
-static void
-finish(struct battle_state *st, struct battle *bt)
-{
-	(void)bt;
-
-	free(st->data);
-}
-
-int
-battle_state_opening_update(struct battle_state_opening *op, struct battle *bt, unsigned int ticks)
-{
-	op->elapsed += ticks;
-
-	/*
-	 * Those function will effectively change state accordingly to the
-	 * order of playing.
-	 */
-	if (op->elapsed >= DELAY)
-		battle_state_check(bt);
-
-	return 0;
-}
-
-void
-battle_state_opening_draw(const struct battle_state_opening *op, const struct battle *bt)
-{
-	assert(op);
-	assert(bt);
-
-	const unsigned int w = window.w;
-	const unsigned int h = window.h / 2;
-	const unsigned int ch = op->elapsed * h / DELAY;
-
-	battle_draw_component(bt, BATTLE_COMPONENT_BACKGROUND | BATTLE_COMPONENT_ENTITIES);
-
-	/* Draw some bezels opening. */
-	painter_set_color(0x000000ff);
-	painter_draw_rectangle(0, 0, w, h - ch);
-	painter_draw_rectangle(0, h + ch, w, h - ch);
-}
-
-void
-battle_state_opening(struct battle *bt)
-{
-	assert(bt);
-
-	struct self *self;
-
-	self = alloc_new0(sizeof (*self));
-	self->state.data = self;
-	self->state.update = update;
-	self->state.draw = draw;
-	self->state.finish = finish;
-
-	battle_switch(bt, &self->state);
-}
--- a/src/libmlk-rpg/rpg/battle-state-opening.h	Sat Oct 15 21:19:25 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,37 +0,0 @@
-/*
- * battle-state-opening.h -- battle state (opening)
- *
- * Copyright (c) 2020-2022 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 MLK_RPG_BATTLE_STATE_OPENING_H
-#define MLK_RPG_BATTLE_STATE_OPENING_H
-
-struct battle;
-
-struct battle_state_opening {
-	unsigned int elapsed;
-};
-
-int
-battle_state_opening_update(struct battle_state_opening *, struct battle *, unsigned int);
-
-void
-battle_state_opening_draw(const struct battle_state_opening *, const struct battle *);
-
-void
-battle_state_opening(struct battle *);
-
-#endif /* !MLK_RPG_BATTLE_STATE_OPENING_H */
--- a/src/libmlk-rpg/rpg/battle-state-rendering.c	Sat Oct 15 21:19:25 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,114 +0,0 @@
-/*
- * battle-state-rendering.c -- battle state (rendering an action)
- *
- * Copyright (c) 2020-2022 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 <stdlib.h>
-
-#include <mlk/core/alloc.h>
-#include <mlk/core/drawable.h>
-
-#include "battle-state-rendering.h"
-#include "battle.h"
-
-struct self {
-	struct battle_state_rendering data;
-	struct battle_state state;
-};
-
-static int
-update(struct battle_state *st, struct battle *bt, unsigned int ticks)
-{
-	battle_state_rendering_update(st->data, bt, ticks);
-
-	return 0;
-}
-
-static void
-draw(const struct battle_state *st, const struct battle *bt)
-{
-	battle_state_rendering_draw(st->data, bt);
-}
-
-static void
-finish(struct battle_state *st, struct battle *bt)
-{
-	(void)bt;
-
-	battle_state_rendering_finish(st->data);
-	free(st->data);
-}
-
-void
-battle_state_rendering_init(struct battle_state_rendering *rdr, struct drawable *dw)
-{
-	assert(rdr);
-	assert(dw);
-
-	rdr->drawable = dw;
-}
-
-int
-battle_state_rendering_update(struct battle_state_rendering *rdr, struct battle *bt, unsigned int ticks)
-{
-	assert(rdr);
-	assert(battle_ok(bt));
-
-	battle_update_component(bt, BATTLE_COMPONENT_ALL, ticks);
-
-	if (drawable_update(rdr->drawable, ticks)) {
-		drawable_end(rdr->drawable);
-		return 1;
-	}
-
-	return 0;
-}
-
-void
-battle_state_rendering_draw(const struct battle_state_rendering *rdr, const struct battle *bt)
-{
-	assert(rdr);
-
-	battle_draw_component(bt, BATTLE_COMPONENT_ALL);
-	drawable_draw(rdr->drawable);
-}
-
-void
-battle_state_rendering_finish(struct battle_state_rendering *rdr)
-{
-	assert(rdr);
-
-	drawable_finish(rdr->drawable);
-}
-
-void
-battle_state_rendering(struct battle *bt, struct drawable *dw)
-{
-	assert(bt);
-	assert(dw);
-
-	struct self *self;
-
-	self = alloc_new0(sizeof (*self));
-	self->state.data = self;
-	self->state.update = update;
-	self->state.draw = draw;
-	self->state.finish = finish;
-
-	battle_state_rendering_init(&self->data, dw);
-	battle_switch(bt, &self->state);
-}
--- a/src/libmlk-rpg/rpg/battle-state-rendering.h	Sat Oct 15 21:19:25 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,44 +0,0 @@
-/*
- * battle-state-rendering.h -- battle state (rendering an action)
- *
- * Copyright (c) 2020-2022 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 MLK_RPG_BATTLE_STATE_RENDERING_H
-#define MLK_RPG_BATTLE_STATE_RENDERING_H
-
-struct battle;
-struct drawable;
-
-struct battle_state_rendering {
-	struct drawable *drawable;
-};
-
-void
-battle_state_rendering_init(struct battle_state_rendering *, struct drawable *);
-
-int
-battle_state_rendering_update(struct battle_state_rendering *, struct battle *, unsigned int);
-
-void
-battle_state_rendering_draw(const struct battle_state_rendering *, const struct battle *);
-
-void
-battle_state_rendering_finish(struct battle_state_rendering *);
-
-void
-battle_state_rendering(struct battle *, struct drawable *);
-
-#endif /* !MLK_RPG_BATTLE_STATE_RENDERING_H */
--- a/src/libmlk-rpg/rpg/battle-state-selection.c	Sat Oct 15 21:19:25 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,231 +0,0 @@
-/*
- * battle-state-selection.c -- battle state (selection)
- *
- * Copyright (c) 2020-2022 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 <stdlib.h>
-#include <string.h>
-
-#include <mlk/core/alloc.h>
-#include <mlk/core/event.h>
-#include <mlk/core/panic.h>
-#include <mlk/core/sprite.h>
-#include <mlk/core/util.h>
-
-#include <mlk/ui/theme.h>
-
-#include "battle-bar.h"
-#include "battle-state-item.h"
-#include "battle-state-menu.h"
-#include "battle-state-selection.h"
-#include "battle-state.h"
-#include "battle.h"
-#include "character.h"
-#include "inventory.h"
-#include "selection.h"
-#include "spell.h"
-
-struct self {
-	struct battle_state_selection data;
-	struct battle_state state;
-};
-
-static void
-select_adj_in(struct battle_state_selection *slt, struct battle_entity **entities, size_t entitiesz, int step)
-{
-	assert(slt->select.index_character != (unsigned int)-1);
-
-	unsigned int newselection = slt->select.index_character;
-
-	if (step < 0) {
-		while (newselection > 0) {
-			if (character_ok(entities[--newselection]->ch)) {
-				slt->select.index_character = newselection;
-				break;
-			}
-		}
-	} else {
-		while (newselection < entitiesz) {
-			if (character_ok(entities[++newselection]->ch)) {
-				slt->select.index_character = newselection;
-				break;
-			}
-		}
-	}
-}
-
-static void
-select_adj(struct battle_state_selection *slt, const struct battle *bt, int step)
-{
-	if (slt->select.index_side == 0)
-		select_adj_in(slt, bt->enemies, bt->enemiesz, step);
-	else
-		select_adj_in(slt, bt->team, bt->teamsz, step);
-}
-
-static void
-handle_keydown(struct battle_state_selection *stl, struct battle *bt, const union event *ev)
-{
-	assert(ev->type == EVENT_KEYDOWN);
-
-	switch (ev->key.key) {
-	case KEY_ESCAPE:
-		battle_state_menu(bt);
-		break;
-	case KEY_ENTER:
-		battle_bar_select(bt->bar, bt, &stl->select);
-		break;
-	case KEY_LEFT:
-		if (stl->select.allowed_sides & SELECTION_SIDE_ENEMY)
-			stl->select.index_side = 0;
-		break;
-	case KEY_RIGHT:
-		if (stl->select.allowed_sides & SELECTION_SIDE_TEAM)
-			stl->select.index_side = 1;
-		break;
-	case KEY_UP:
-		select_adj(stl, bt, -1);
-		break;
-	case KEY_DOWN:
-		select_adj(stl, bt, +1);
-		break;
-	case KEY_TAB:
-		if (stl->select.allowed_kinds == SELECTION_KIND_BOTH)
-			stl->select.index_character = -1;
-		break;
-	default:
-		break;
-	}
-}
-
-static void
-draw_cursor(const struct battle *bt, const struct battle_entity *et)
-{
-	const struct theme *theme = BATTLE_THEME(bt);
-	const struct sprite *cursor = theme->sprites[THEME_SPRITE_CURSOR];
-	int x, y;
-	unsigned int lh;
-
-	if (!cursor)
-		return;
-
-	label_query(&et->name, NULL, &lh);
-
-	x = et->name.x - cursor->cellw - theme->padding;
-	y = et->name.y + (((int)(lh) - (int)(cursor->cellh)) / 2);
-
-	sprite_draw(cursor, 1, 2, x, y);
-}
-
-static void
-draw_cursors(const struct battle *bt,
-             struct battle_entity * const *entities,
-             size_t entitiesz)
-{
-	for (size_t i = 0; i < entitiesz; ++i) {
-		const struct battle_entity *et = entities[i];
-
-		if (et && character_ok(et->ch))
-			draw_cursor(bt, et);
-	}
-}
-
-static void
-handle(struct battle_state *st, struct battle *bt, const union event *ev)
-{
-	battle_state_selection_handle(st->data, bt, ev);
-}
-
-static void
-draw(const struct battle_state *st, const struct battle *bt)
-{
-	battle_state_selection_draw(st->data, bt);
-}
-
-static void
-finish(struct battle_state *st, struct battle *bt)
-{
-	(void)bt;
-
-	free(st->data);
-}
-
-void
-battle_state_selection_init(struct battle_state_selection *stl, const struct selection *select)
-{
-	assert(stl);
-	assert(select);
-
-	memcpy(&stl->select, select, sizeof (*select));
-}
-
-void
-battle_state_selection_handle(struct battle_state_selection *stl, struct battle *bt, const union event *ev)
-{
-	assert(stl);
-	assert(bt);
-	assert(ev);
-
-	switch (ev->type) {
-	case EVENT_KEYDOWN:
-		handle_keydown(stl, bt, ev);
-		break;
-	default:
-		break;
-	}
-}
-
-void
-battle_state_selection_draw(const struct battle_state_selection *stl, const struct battle *bt)
-{
-	assert(stl);
-	assert(bt);
-
-	battle_draw_component(bt, BATTLE_COMPONENT_ALL);
-
-	if (stl->select.index_character == -1U) {
-		/* All selected. */
-		if (stl->select.index_side == 0)
-			draw_cursors(bt, bt->enemies, bt->enemiesz);
-		else
-			draw_cursors(bt, bt->team, bt->teamsz);
-	} else {
-		/* Select one. */
-		if (stl->select.index_side == 0)
-			draw_cursor(bt, bt->enemies[stl->select.index_character]);
-		else
-			draw_cursor(bt, bt->team[stl->select.index_character]);
-	}
-}
-
-void
-battle_state_selection(struct battle *bt, const struct selection *select)
-{
-	assert(bt);
-	assert(select);
-
-	struct self *self;
-
-	self = alloc_new0(sizeof (*self));
-	self->state.data = self;
-	self->state.handle = handle;
-	self->state.draw = draw;
-	self->state.finish = finish;
-
-	battle_state_selection_init(&self->data, select);
-	battle_switch(bt, &self->state);
-}
--- a/src/libmlk-rpg/rpg/battle-state-selection.h	Sat Oct 15 21:19:25 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,44 +0,0 @@
-/*
- * battle-state-selection.h -- battle state (selection)
- *
- * Copyright (c) 2020-2022 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 MLK_RPG_BATTLE_STATE_SELECTION_H
-#define MLK_RPG_BATTLE_STATE_SELECTION_H
-
-#include <rpg/selection.h>
-
-struct battle;
-
-union event;
-
-struct battle_state_selection {
-	struct selection select;
-};
-
-void
-battle_state_selection_init(struct battle_state_selection *, const struct selection *);
-
-void
-battle_state_selection_handle(struct battle_state_selection *, struct battle *, const union event *);
-
-void
-battle_state_selection_draw(const struct battle_state_selection *, const struct battle *);
-
-void
-battle_state_selection(struct battle *, const struct selection *);
-
-#endif /* !MLK_RPG_BATTLE_STATE_SELECTION_H */
--- a/src/libmlk-rpg/rpg/battle-state-victory.c	Sat Oct 15 21:19:25 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,136 +0,0 @@
-/*
- * battle-state-victory.c -- battle state (victory)
- *
- * Copyright (c) 2020-2022 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 <mlk/core/alloc.h>
-#include <mlk/core/music.h>
-#include <mlk/core/panic.h>
-#include <mlk/core/window.h>
-
-#include "battle-state-closing.h"
-#include "battle-state-victory.h"
-#include "battle-state.h"
-#include "battle.h"
-
-struct self {
-	struct battle_state_victory data;
-	struct battle_state state;
-};
-
-static void
-handle(struct battle_state *st, struct battle *bt, const union event *ev)
-{
-	(void)bt;
-
-	battle_state_victory_handle(st->data, ev);
-}
-
-static int
-update(struct battle_state *st, struct battle *bt, unsigned int ticks)
-{
-	(void)bt;
-
-	return battle_state_victory_update(st->data, bt, ticks);
-}
-
-static void
-draw(const struct battle_state *st, const struct battle *bt)
-{
-	(void)bt;
-
-	battle_state_victory_draw(st->data, bt);
-}
-
-void
-battle_state_victory_init(struct battle_state_victory *vic, struct battle *bt)
-{
-	assert(bt);
-
-	vic->text = "Victory!";
-
-	vic->msg.lines = &vic->text;
-	vic->msg.linesz = 1;
-	vic->msg.theme = bt->theme;
-	vic->msg.flags = MESSAGE_FLAGS_AUTOMATIC |
-	                 MESSAGE_FLAGS_FADEIN |
-	                 MESSAGE_FLAGS_FADEOUT;
-	vic->msg.timeout = MESSAGE_TIMEOUT_DEFAULT;
-	vic->msg.delay = MESSAGE_DELAY_DEFAULT;
-
-	message_start(&vic->msg);
-	message_query(&vic->msg, NULL, &vic->msg.h);
-
-	vic->msg.w = window.w * 0.6;
-	vic->msg.y = window.h * 0.1;
-	vic->msg.x = (window.w - vic->msg.w) / 2;
-
-	bt->status = BATTLE_STATUS_WON;
-
-	if (bt->music[1])
-		music_play(bt->music[1], MUSIC_NONE);
-}
-
-void
-battle_state_victory_handle(struct battle_state_victory *vic, const union event *ev)
-{
-	assert(vic);
-
-	message_handle(&vic->msg, ev);
-}
-
-int
-battle_state_victory_update(struct battle_state_victory *vic, struct battle *bt, unsigned int ticks)
-{
-	assert(vic);
-	assert(bt);
-
-	battle_update_component(bt, BATTLE_COMPONENT_ALL, ticks);
-
-	if (message_update(&vic->msg, ticks))
-		battle_state_closing(bt);
-
-	return 0;
-}
-
-void
-battle_state_victory_draw(const struct battle_state_victory *vic, const struct battle *bt)
-{
-	assert(vic);
-
-	battle_draw_component(bt, BATTLE_COMPONENT_ALL);
-	message_draw(&vic->msg);
-}
-
-void
-battle_state_victory(struct battle *bt)
-{
-	assert(bt);
-
-	struct self *self;
-
-	/* TODO: compute money, xp and drop. */
-	self = alloc_new0(sizeof (*self));
-	self->state.data = self;
-	self->state.handle = handle;
-	self->state.update = update;
-	self->state.draw = draw;
-
-	battle_state_victory_init(&self->data, bt);
-	battle_switch(bt, &self->state);
-}
--- a/src/libmlk-rpg/rpg/battle-state-victory.h	Sat Oct 15 21:19:25 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,48 +0,0 @@
-/*
- * battle-state-victory.h -- battle state (victory)
- *
- * Copyright (c) 2020-2022 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 MLK_RPG_BATTLE_STATE_VICTORY_H
-#define MLK_RPG_BATTLE_STATE_VICTORY_H
-
-#include <rpg/message.h>
-
-struct battle;
-
-union event;
-
-struct battle_state_victory {
-	const char *text;
-	struct message msg;
-};
-
-void
-battle_state_victory_init(struct battle_state_victory *, struct battle *);
-
-void
-battle_state_victory_handle(struct battle_state_victory *, const union event *);
-
-int
-battle_state_victory_update(struct battle_state_victory *, struct battle *, unsigned int);
-
-void
-battle_state_victory_draw(const struct battle_state_victory *, const struct battle *);
-
-void
-battle_state_victory(struct battle *);
-
-#endif /* !MLK_RPG_BATTLE_STATE_VICTORY_H */
--- a/src/libmlk-rpg/rpg/battle-state.c	Sat Oct 15 21:19:25 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,64 +0,0 @@
-/*
- * battle-state.c -- battle abstract state
- *
- * Copyright (c) 2020-2022 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 "battle-state.h"
-
-void
-battle_state_handle(struct battle_state *st, struct battle *bt, const union event *ev)
-{
-	assert(st);
-	assert(bt);
-	assert(ev);
-
-	if (st->handle)
-		st->handle(st, bt, ev);
-}
-
-int
-battle_state_update(struct battle_state *st, struct battle *bt, unsigned int ticks)
-{
-	assert(st);
-	assert(bt);
-
-	if (st->update)
-		return st->update(st, bt, ticks);
-
-	return 0;
-}
-
-void
-battle_state_draw(const struct battle_state *st, const struct battle *bt)
-{
-	assert(st);
-	assert(bt);
-
-	if (st->draw)
-		st->draw(st, bt);
-}
-
-void
-battle_state_finish(struct battle_state *st, struct battle *bt)
-{
-	assert(st);
-	assert(bt);
-
-	if (st->finish)
-		st->finish(st, bt);
-}
--- a/src/libmlk-rpg/rpg/battle-state.h	Sat Oct 15 21:19:25 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,55 +0,0 @@
-/*
- * battle-state.h -- battle abstract state
- *
- * Copyright (c) 2020-2022 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 MLK_RPG_BATTLE_STATE_H
-#define MLK_RPG_BATTLE_STATE_H
-
-#include <mlk/core/core.h>
-
-struct battle;
-struct character;
-struct inventory_slot;
-struct selection;
-
-union event;
-
-struct battle_state {
-	void *data;
-	void (*handle)(struct battle_state *, struct battle *, const union event *);
-	int (*update)(struct battle_state *, struct battle *, unsigned int);
-	void (*draw)(const struct battle_state *, const struct battle *);
-	void (*finish)(struct battle_state *, struct battle *);
-};
-
-CORE_BEGIN_DECLS
-
-void
-battle_state_handle(struct battle_state *, struct battle *, const union event *);
-
-int
-battle_state_update(struct battle_state *, struct battle *, unsigned int);
-
-void
-battle_state_draw(const struct battle_state *, const struct battle *);
-
-void
-battle_state_finish(struct battle_state *, struct battle *);
-
-CORE_END_DECLS
-
-#endif /* !MLK_RPG_BATTLE_STATE_H */
--- a/src/libmlk-rpg/rpg/battle.c	Sat Oct 15 21:19:25 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,502 +0,0 @@
-/*
- * battle.c -- battles
- *
- * Copyright (c) 2020-2022 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 <stdlib.h>
-#include <string.h>
-
-#include <mlk/core/alloc.h>
-#include <mlk/core/event.h>
-#include <mlk/core/font.h>
-#include <mlk/core/music.h>
-#include <mlk/core/sprite.h>
-#include <mlk/core/texture.h>
-#include <mlk/core/trace.h>
-#include <mlk/core/util.h>
-#include <mlk/core/window.h>
-
-#include <mlk/ui/align.h>
-#include <mlk/ui/frame.h>
-#include <mlk/ui/label.h>
-#include <mlk/ui/theme.h>
-
-#include "battle-bar.h"
-#include "battle-indicator.h"
-#include "battle-state-ai.h"
-#include "battle-state-attacking.h"
-#include "battle-state-check.h"
-#include "battle-state-menu.h"
-#include "battle-state-opening.h"
-#include "battle-state.h"
-#include "battle.h"
-#include "character.h"
-#include "inventory.h"
-#include "item.h"
-#include "spell.h"
-
-struct indicator {
-	struct drawable dw;
-	struct battle_indicator bti;
-};
-
-static int
-indicator_update(struct drawable *dw, unsigned int ticks)
-{
-	struct indicator *id = dw->data;
-
-	return battle_indicator_update(&id->bti, ticks);
-}
-
-static void
-indicator_draw(struct drawable *dw)
-{
-	const struct indicator *id = dw->data;
-
-	battle_indicator_draw(&id->bti, dw->x, dw->y);
-}
-
-static void
-indicator_free(struct drawable *dw)
-{
-	struct indicator *id = dw->data;
-
-	battle_indicator_finish(&id->bti);
-	free(id);
-}
-
-static struct battle_entity *
-find(struct battle *bt, const struct character *ch)
-{
-	struct battle_entity *et;
-
-	BATTLE_TEAM_FOREACH(bt, et)
-		if (et->ch == ch)
-			return et;
-	BATTLE_ENEMY_FOREACH(bt, et)
-		if (et->ch == ch)
-			return et;
-
-	return NULL;
-}
-
-static struct battle_entity *
-random_select(struct battle_entity **group, size_t groupsz)
-{
-	struct battle_entity *ret = NULL, *et = NULL;
-
-	do {
-		et = group[util_nrand(0, groupsz - 1)];
-
-		if (et && et->ch)
-			ret = et;
-	} while (!ret);
-
-	return ret;
-}
-
-static int
-cmp_order(const void *d1, const void *d2)
-{
-	const struct battle_entity *et1 = *(const struct battle_entity **)d1;
-	const struct battle_entity *et2 = *(const struct battle_entity **)d2;
-
-	return et2->ch->agt < et1->ch->agt;
-}
-
-static int
-is_team(const struct battle *bt, const struct character *ch)
-{
-	for (size_t i = 0; i < bt->teamsz; ++i)
-		if (bt->team[i] && bt->team[i]->ch == ch)
-			return 1;
-
-	return 0;
-}
-
-static void
-positionate_name(struct battle_entity *et, const struct battle *bt)
-{
-	unsigned int lw;
-	struct sprite *sprite;
-
-	/* Show the character name below its sprite. */
-	sprite = et->ch->sprites[CHARACTER_SPRITE_NORMAL];
-
-	et->name.text = et->ch->name;
-	et->name.flags = LABEL_FLAGS_SHADOW;
-	label_query(&et->name, &lw, NULL);
-	et->name.y = et->y + sprite->cellh + BATTLE_THEME(bt)->padding;
-	et->name.x = et->x + (sprite->cellw / 2) - (lw / 2);
-}
-
-static void
-positionate_names(struct battle *bt)
-{
-	struct battle_entity *et;
-
-	BATTLE_TEAM_FOREACH(bt, et)
-		if (character_ok(et->ch))
-			positionate_name(et, bt);
-	BATTLE_ENEMY_FOREACH(bt, et)
-		if (character_ok(et->ch))
-			positionate_name(et, bt);
-}
-
-static void
-positionate_team(struct battle *bt)
-{
-	struct battle_entity *et;
-	unsigned int requirement = 0, nmemb = 0, spacing;
-	int x, y;
-
-	BATTLE_TEAM_FOREACH(bt, et) {
-		/* Stop in case any member of the team has been positionated. */
-		if (et->x != 0 || et->y != 0)
-			return;
-
-		if (battle_entity_ok(bt->team[i])) {
-			nmemb++;
-			requirement += et->ch->sprites[CHARACTER_SPRITE_NORMAL]->cellh;
-		}
-	}
-
-	/* TODO: compute a correct x placement. */
-	spacing = (window.h - requirement) / (nmemb + 1);
-	x = window.w - 200;
-	y = spacing;
-
-	BATTLE_TEAM_FOREACH(bt, et) {
-		if (battle_entity_ok(et)) {
-			et->x = x;
-			et->y = y;
-			y += et->ch->sprites[CHARACTER_SPRITE_NORMAL]->cellh + spacing;
-		}
-	}
-}
-
-static void
-draw_entities(const struct battle *bt, struct battle_entity **entities, size_t entitiesz)
-{
-	for (size_t i = 0; i < entitiesz; ++i) {
-		if (battle_entity_ok(entities[i]))
-			battle_entity_draw(entities[i], bt);
-	}
-}
-
-static void
-update_entities(struct battle_entity **entities, size_t entitiesz, unsigned int ticks)
-{
-	for (size_t i = 0; i < entitiesz; ++i) {
-		if (battle_entity_ok(entities[i]))
-			battle_entity_update(entities[i], ticks);
-	}
-}
-
-void
-battle_init(struct battle *bt)
-{
-	assert(bt);
-
-	memset(bt, 0, sizeof (*bt));
-}
-
-int
-battle_ok(const struct battle *bt)
-{
-	return bt && bt->state && bt->bar && bt->enemiesz && bt->team;
-}
-
-void
-battle_start(struct battle *bt)
-{
-	assert(bt);
-
-	struct battle_entity *et;
-
-	BATTLE_TEAM_FOREACH(bt, et)
-		if (battle_entity_ok(et))
-			battle_entity_init(et);
-	BATTLE_ENEMY_FOREACH(bt, et)
-		if (battle_entity_ok(et))
-			battle_entity_init(et);
-
-	positionate_team(bt);
-	positionate_names(bt);
-
-	/* Start the state "opening" animation. */
-	battle_state_opening(bt);
-
-	/* Play music if present. */
-	if (bt->music[0])
-		music_play(bt->music[0], MUSIC_LOOP);
-
-	battle_order(bt);
-}
-
-void
-battle_switch(struct battle *bt, struct battle_state *st)
-{
-	assert(bt);
-	assert(st);
-
-	if (bt->state)
-		battle_state_finish(bt->state, bt);
-
-	bt->state = st;
-}
-
-void
-battle_order(struct battle *bt)
-{
-	struct battle_entity **porder;
-
-	/* Create a pointer list to every entity. */
-	bt->order = alloc_rearray0(bt->order, bt->ordersz,
-	    bt->teamsz + bt->enemiesz, sizeof (*bt->order));
-	bt->ordersz = bt->teamsz + bt->enemiesz;
-	bt->ordercur = porder = bt->order;
-
-	for (size_t i = 0; i < bt->teamsz; ++i)
-		if (bt->team[i] && character_ok(bt->team[i]->ch))
-			*porder++ = bt->team[i];
-	for (size_t i = 0; i < bt->enemiesz; ++i)
-		if (bt->enemies[i] && character_ok(bt->enemies[i]->ch))
-			*porder++ = bt->enemies[i];
-
-	/* Now sort. */
-	qsort(bt->order, bt->ordersz, sizeof (*bt->order), cmp_order);
-}
-
-struct battle_entity *
-battle_current(const struct battle *bt)
-{
-	assert(bt);
-
-	return *bt->ordercur;
-}
-
-size_t
-battle_index(const struct battle *bt)
-{
-	assert(bt);
-
-	return bt->ordercur - bt->order;
-}
-
-void
-battle_attack(struct battle *bt,
-              struct character *source,
-              struct character *target)
-{
-	assert(bt);
-	assert(character_ok(source));
-
-	/* Target is empty? select randomly. */
-	if (!target) {
-		if (is_team(bt, source))
-			target = random_select(bt->enemies, bt->enemiesz)->ch;
-		else
-			target = random_select(bt->team, bt->teamsz)->ch;
-	}
-
-	battle_state_attacking(battle_find(bt, source), battle_find(bt, target), bt);
-}
-
-void
-battle_cast(struct battle *bt,
-            struct character *source,
-            const struct spell *spell,
-            const struct selection *selection)
-{
-	assert(bt);
-	assert(source);
-	assert(spell);
-	assert((unsigned int)source->mp >= spell->mp);
-
-	/* TODO: animate. */
-	source->mp -= spell->mp;
-	spell_action(spell, bt, source, selection);
-}
-
-void
-battle_use(struct battle *bt, const struct item *item, struct character *owner, struct character *target)
-{
-	assert(bt);
-	assert(item);
-	assert(owner);
-	assert(target);
-
-	/*
-	 * Change the state to check prior to execute the item so it can change to something else
-	 * if needed.
-	 */
-	battle_state_check(bt);
-
-	inventory_consume(bt->inventory, item, 1);
-	item_exec_battle(item, bt, owner, target);
-}
-
-void
-battle_next(struct battle *bt)
-{
-	assert(bt);
-
-	if (!bt->ordercur)
-		battle_order(bt);
-	else {
-		if (bt->ordercur - bt->order + (size_t)1U >= bt->ordersz)
-			battle_order(bt);
-		else
-			bt->ordercur++;
-	}
-
-	if (is_team(bt, battle_current(bt)->ch)) {
-		battle_bar_start(bt->bar, bt);
-		battle_state_menu(bt);
-	} else
-		battle_state_ai(bt);
-}
-
-struct battle_entity *
-battle_find(struct battle *bt, const struct character *ch)
-{
-	assert(bt);
-
-	return find(bt, ch);
-}
-
-void
-battle_indicator_hp(struct battle *bt, const struct character *target, long amount)
-{
-	assert(bt);
-	assert(target);
-
-	const struct battle_entity *et = find(bt, target);
-	struct indicator *id;
-
-	if (!(bt->effects)) {
-		tracef("unable to add id without a drawable_stack");
-		return;
-	}
-
-	id = alloc_new0(sizeof (*id));
-	id->bti.color = BATTLE_INDICATOR_HP_COLOR;
-	id->bti.amount = labs(amount);
-
-	/* TODO: positionate better. */
-	id->dw.x = et->x + target->sprites[CHARACTER_SPRITE_NORMAL]->cellw;
-	id->dw.y = et->y + target->sprites[CHARACTER_SPRITE_NORMAL]->cellh;
-	id->dw.data = id;
-	id->dw.update = indicator_update;
-	id->dw.draw = indicator_draw;
-	id->dw.finish = indicator_free;
-
-	battle_indicator_start(&id->bti);
-
-	if (drawable_stack_add(bt->effects, &id->dw) < 0)
-		drawable_finish(&id->dw);
-}
-
-void
-battle_handle_component(struct battle *bt, const union event *ev, enum battle_component comp)
-{
-	assert(bt);
-	assert(ev);
-
-	if (comp & BATTLE_COMPONENT_BAR)
-		battle_bar_handle(bt->bar, bt, ev);
-	if ((comp & BATTLE_COMPONENT_ACTIONS) && bt->actions)
-		action_stack_handle(bt->actions, ev);
-}
-
-void
-battle_handle(struct battle *bt, const union event *ev)
-{
-	assert(bt);
-	assert(ev);
-
-	battle_state_handle(bt->state, bt, ev);
-}
-
-void
-battle_update_component(struct battle *bt, unsigned int ticks, enum battle_component comp)
-{
-	assert(bt);
-
-	if (comp & BATTLE_COMPONENT_ENTITIES) {
-		update_entities(bt->team, bt->teamsz, ticks);
-		update_entities(bt->enemies, bt->enemiesz, ticks);
-	}
-	if (comp & BATTLE_COMPONENT_BAR)
-		battle_bar_update(bt->bar, bt, ticks);
-	if ((comp & BATTLE_COMPONENT_ACTIONS) && bt->actions)
-		action_stack_update(bt->actions, ticks);
-	if ((comp & BATTLE_COMPONENT_DRAWABLES) && bt->effects)
-		drawable_stack_update(bt->effects, ticks);
-}
-
-int
-battle_update(struct battle *bt, unsigned int ticks)
-{
-	assert(bt && bt->state);
-
-	return battle_state_update(bt->state, bt, ticks);
-}
-
-void
-battle_draw_component(const struct battle *bt, enum battle_component comp)
-{
-	assert(bt);
-
-	if ((comp & BATTLE_COMPONENT_BACKGROUND) && texture_ok(bt->background))
-		texture_scale(bt->background,
-		    0, 0, bt->background->w, bt->background->h,
-		    0, 0, window.w, window.h,
-		    0.f);
-	if (comp & BATTLE_COMPONENT_ENTITIES) {
-		draw_entities(bt, bt->team, bt->teamsz);
-		draw_entities(bt, bt->enemies, bt->enemiesz);
-	}
-	if (comp & BATTLE_COMPONENT_BAR)
-		battle_bar_draw(bt->bar, bt);
-	if ((comp & BATTLE_COMPONENT_ACTIONS) && bt->actions)
-		action_stack_draw(bt->actions);
-	if ((comp & BATTLE_COMPONENT_DRAWABLES) && bt->effects)
-		drawable_stack_draw(bt->effects);
-}
-
-void
-battle_draw(const struct battle *bt)
-{
-	assert(battle_ok(bt));
-
-	battle_state_draw(bt->state, bt);
-}
-
-void
-battle_finish(struct battle *bt)
-{
-	assert(bt);
-
-	if (bt->state)
-		battle_state_finish(bt->state, bt);
-
-	free(bt->order);
-	memset(bt, 0, sizeof (*bt));
-}
--- a/src/libmlk-rpg/rpg/battle.h	Sat Oct 15 21:19:25 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,153 +0,0 @@
-/*
- * battle.h -- battles
- *
- * Copyright (c) 2020-2022 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 MLK_RPG_BATTLE_H
-#define MLK_RPG_BATTLE_H
-
-#include <mlk/core/action.h>
-#include <mlk/core/action-stack.h>
-#include <mlk/core/core.h>
-#include <mlk/core/drawable.h>
-#include <mlk/core/drawable-stack.h>
-
-#include <mlk/ui/frame.h>
-#include <mlk/ui/gridmenu.h>
-
-#include "battle-entity.h"
-#include "battle-state.h"
-#include "selection.h"
-#include "spell.h"
-
-union event;
-
-struct battle_bar;
-struct character;
-struct inventory;
-struct item;
-struct music;
-struct selection;
-struct spell;
-struct theme;
-
-#define BATTLE_TEAM_FOREACH(bt, iter) \
-	for (size_t i = 0; i < (bt)->teamsz && ((iter) = (bt)->team[i]); ++i)
-#define BATTLE_ENEMY_FOREACH(bt, iter) \
-	for (size_t i = 0; i < (bt)->enemiesz && ((iter) = (bt)->enemies[i]); ++i)
-
-#define BATTLE_THEME(bt) ((bt)->theme ? (bt)->theme : theme_default())
-
-enum battle_status {
-	BATTLE_STATUS_NONE,
-	BATTLE_STATUS_RUNNING,
-	BATTLE_STATUS_WON,
-	BATTLE_STATUS_LOST,
-};
-
-enum battle_component {
-	BATTLE_COMPONENT_BACKGROUND     = (1 << 0),
-	BATTLE_COMPONENT_ENTITIES       = (1 << 1),
-	BATTLE_COMPONENT_BAR            = (1 << 2),
-	BATTLE_COMPONENT_ACTIONS        = (1 << 3),
-	BATTLE_COMPONENT_DRAWABLES      = (1 << 4),
-	BATTLE_COMPONENT_ALL            = 0xff
-};
-
-struct battle {
-	struct battle_state *state;
-	enum battle_status status;
-	struct battle_entity **team;
-	size_t teamsz;
-	struct battle_entity **enemies;
-	size_t enemiesz;
-	struct battle_entity **order;
-	struct battle_entity **ordercur;
-	size_t ordersz;
-	struct texture *background;
-	struct music *music[3];
-	struct theme *theme;
-	struct drawable_stack *effects;
-	struct action_stack *actions;
-	struct inventory *inventory;
-	struct battle_bar *bar;
-};
-
-CORE_BEGIN_DECLS
-
-void
-battle_init(struct battle *);
-
-int
-battle_ok(const struct battle *);
-
-void
-battle_start(struct battle *);
-
-void
-battle_next(struct battle *);
-
-struct battle_entity *
-battle_find(struct battle *, const struct character *);
-
-void
-battle_switch(struct battle *, struct battle_state *);
-
-void
-battle_order(struct battle *);
-
-struct battle_entity *
-battle_current(const struct battle *);
-
-size_t
-battle_index(const struct battle *);
-
-void
-battle_attack(struct battle *, struct character *, struct character *);
-
-void
-battle_cast(struct battle *, struct character *, const struct spell *, const struct selection *);
-
-void
-battle_use(struct battle *, const struct item *, struct character *, struct character *);
-
-void
-battle_indicator_hp(struct battle *, const struct character *, long);
-
-void
-battle_handle_component(struct battle *, const union event *, enum battle_component);
-
-void
-battle_handle(struct battle *, const union event *);
-
-void
-battle_update_component(struct battle *, unsigned int, enum battle_component);
-
-int
-battle_update(struct battle *, unsigned int);
-
-void
-battle_draw_component(const struct battle *, enum battle_component);
-
-void
-battle_draw(const struct battle *);
-
-void
-battle_finish(struct battle *);
-
-CORE_END_DECLS
-
-#endif /* MLK_RPG_BATTLE_H */
--- a/src/libmlk-rpg/rpg/character.c	Sat Oct 15 21:19:25 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,122 +0,0 @@
-/*
- * character.c -- character definition
- *
- * Copyright (c) 2020-2022 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 <mlk/core/sprite.h>
-
-#include <assets/sql/character-save.h>
-#include <assets/sql/character-load.h>
-
-#include "character.h"
-#include "equipment.h"
-#include "save.h"
-
-int
-character_ok(const struct character *ch)
-{
-	return ch && ch->name && ch->reset && sprite_ok(ch->sprites[CHARACTER_SPRITE_NORMAL]);
-}
-
-const char *
-character_status_string(enum character_status status)
-{
-	/*
-	 * We expect the user to only specify one status as character_status
-	 * is a bitmask.
-	 */
-	switch (status) {
-	case CHARACTER_STATUS_POISON:
-		return "poison";
-	default:
-		return "normal";
-	}
-}
-
-void
-character_reset(struct character *ch)
-{
-	assert(character_ok(ch));
-
-	ch->reset(ch);
-
-	/* For all equipments, apply its equip function. */
-	for (int i = 0; i < CHARACTER_EQUIPMENT_NUM; ++i)
-		if (ch->equipments[i])
-			equipment_equip(ch->equipments[i], ch);
-}
-
-void
-character_exec(struct character *ch, struct battle *bt)
-{
-	assert(character_ok(ch));
-
-	if (ch->exec)
-		ch->exec(ch, bt);
-}
-
-int
-character_save(const struct character *ch, struct save *s)
-{
-	assert(ch);
-	assert(save_ok(s));
-
-	return save_exec(s, (const char *)assets_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
-	);
-}
-
-int
-character_load(struct character *ch, struct save *s)
-{
-	assert(ch);
-	assert(save_ok(s));
-
-	struct save_stmt stmt;
-	enum save_stmt_errno ret;
-
-	if (save_stmt_init(&stmt, s, (const char *)assets_character_load, "s", ch->name) < 0)
-		return -1;
-
-	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_ROW;
-
-	save_stmt_finish(&stmt);
-
-	return ret ? 0 : -1;
-}
--- a/src/libmlk-rpg/rpg/character.h	Sat Oct 15 21:19:25 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,109 +0,0 @@
-/*
- * character.h -- character definition
- *
- * Copyright (c) 2020-2022 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 MLK_RPG_CHARACTER_H
-#define MLK_RPG_CHARACTER_H
-
-#include <mlk/core/core.h>
-
-#define CHARACTER_SPELL_MAX (64)
-
-struct battle;
-struct save;
-struct sprite;
-struct spell;
-
-enum character_status {
-	CHARACTER_STATUS_NORMAL,
-	CHARACTER_STATUS_POISON         = (1 << 0)
-};
-
-enum character_sprite {
-	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
-};
-
-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;
-	enum character_status status;
-	unsigned int level;
-	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;
-
-	struct sprite *sprites[CHARACTER_SPRITE_NUM];
-	const struct equipment *equipments[CHARACTER_EQUIPMENT_NUM];
-	const struct spell *spells[CHARACTER_SPELL_MAX];
-
-	void (*reset)(struct character *owner);
-	void (*exec)(struct character *owner, struct battle *bt);
-};
-
-CORE_BEGIN_DECLS
-
-int
-character_ok(const struct character *ch);
-
-const char *
-character_status_string(enum character_status status);
-
-void
-character_reset(struct character *ch);
-
-void
-character_exec(struct character *ch, struct battle *bt);
-
-int
-character_save(const struct character *ch, struct save *s);
-
-int
-character_load(struct character *, struct save *);
-
-CORE_END_DECLS
-
-#endif /* !MLK_RPG_CHARACTER_H */
--- a/src/libmlk-rpg/rpg/equipment.c	Sat Oct 15 21:19:25 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,38 +0,0 @@
-/*
- * equipment.h -- character equipment
- *
- * Copyright (c) 2020-2022 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 "character.h"
-#include "equipment.h"
-
-int
-equipment_ok(const struct equipment *eq)
-{
-	return eq && eq->name && eq->description;
-}
-
-void
-equipment_equip(const struct equipment *eq, struct character *ch)
-{
-	assert(equipment_ok(eq));
-	assert(character_ok(ch));
-
-	if (eq->equip)
-		eq->equip(eq, ch);
-}
--- a/src/libmlk-rpg/rpg/equipment.h	Sat Oct 15 21:19:25 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,66 +0,0 @@
-/*
- * equipment.h -- character equipment
- *
- * Copyright (c) 2020-2022 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 MLK_RPG_EQUIPMENT_H
-#define MLK_RPG_EQUIPMENT_H
-
-#include <mlk/core/core.h>
-
-struct character;
-struct texture;
-
-enum equipment_type {
-	/* Attack weapons. */
-	EQUIPMENT_TYPE_AXE,
-	EQUIPMENT_TYPE_BOW,
-	EQUIPMENT_TYPE_CROSSBOW,
-	EQUIPMENT_TYPE_DAGGER,
-	EQUIPMENT_TYPE_HAMMER,
-	EQUIPMENT_TYPE_SPIKE,
-	EQUIPMENT_TYPE_SWORD,
-	EQUIPMENT_TYPE_WAND,
-
-	/* Defense equipment. */
-	EQUIPMENT_TYPE_GLOVES,
-	EQUIPMENT_TYPE_HELMET,
-	EQUIPMENT_TYPE_SHIELD,
-	EQUIPMENT_TYPE_TOP,
-	EQUIPMENT_TYPE_TROUSERS,
-};
-
-struct equipment {
-	const char *name;
-	const char *description;
-	unsigned int price;
-	enum equipment_type type;
-	struct texture *icon;
-
-	void (*equip)(const struct equipment *, struct character *);
-};
-
-CORE_BEGIN_DECLS
-
-int
-equipment_ok(const struct equipment *);
-
-void
-equipment_equip(const struct equipment *, struct character *);
-
-CORE_END_DECLS
-
-#endif /* !MLK_RPG_EQUIPMENT_H */
--- a/src/libmlk-rpg/rpg/inventory.c	Sat Oct 15 21:19:25 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,69 +0,0 @@
-/*
- * inventory.c -- item inventory
- *
- * Copyright (c) 2020-2022 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 <stddef.h>
-
-#include "inventory.h"
-
-static struct inventory_slot *
-find(struct inventory *iv, const struct item *item)
-{
-	for (size_t i = 0; i < INVENTORY_ITEM_MAX; ++i)
-		if (iv->items[i].item == item)
-			return &iv->items[i];
-
-	return NULL;
-}
-
-int
-inventory_add(struct inventory *iv, const struct item *item, unsigned int amount)
-{
-	assert(iv);
-	assert(item);
-
-	struct inventory_slot *slot;
-
-	/* Find one existing, otherwise find one empty. */
-	if (!(slot = find(iv, item)))
-		slot = find(iv, NULL);
-
-	if (!slot)
-		return 0;
-
-	slot->item = item;
-	slot->amount += amount;
-
-	return -1;
-}
-
-void
-inventory_consume(struct inventory *iv, const struct item *item, unsigned int amount)
-{
-	assert(iv);
-	assert(item);
-
-	struct inventory_slot *slot;
-
-	if ((slot = find(iv, item))) {
-		slot->amount -= amount;
-
-		if (slot->amount == 0)
-			slot->item = NULL;
-	}
-}
--- a/src/libmlk-rpg/rpg/inventory.h	Sat Oct 15 21:19:25 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,47 +0,0 @@
-/*
- * inventory.h -- item inventory
- *
- * Copyright (c) 2020-2022 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 MLK_RPG_INVENTORY_H
-#define MLK_RPG_INVENTORY_H
-
-#include <mlk/core/core.h>
-
-#define INVENTORY_ITEM_MAX (512)
-
-struct item;
-
-struct inventory_slot {
-	unsigned int amount;
-	const struct item *item;
-};
-
-struct inventory {
-	struct inventory_slot items[INVENTORY_ITEM_MAX];
-};
-
-CORE_BEGIN_DECLS
-
-int
-inventory_add(struct inventory *, const struct item *, unsigned int);
-
-void
-inventory_consume(struct inventory *, const struct item *, unsigned int);
-
-CORE_END_DECLS
-
-#endif /* !MLK_RPG_INVENTORY_H */
--- a/src/libmlk-rpg/rpg/item.c	Sat Oct 15 21:19:25 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,44 +0,0 @@
-/*
- * item.c -- inventory items
- *
- * Copyright (c) 2020-2022 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 "item.h"
-
-void
-item_exec_menu(const struct item *item, struct character *ch)
-{
-	assert(item);
-	assert(ch);
-
-	item->exec_menu(item, ch);
-}
-
-void
-item_exec_battle(const struct item *item,
-		 struct battle *bt,
-		 struct character *src,
-		 struct character *tgt)
-{
-	assert(item);
-	assert(bt);
-	assert(src);
-	assert(tgt);
-
-	item->exec_battle(item, bt, src, tgt);
-}
--- a/src/libmlk-rpg/rpg/item.h	Sat Oct 15 21:19:25 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,54 +0,0 @@
-/*
- * item.h -- inventory items
- *
- * Copyright (c) 2020-2022 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 MLK_RPG_ITEM_H
-#define MLK_RPG_ITEM_H
-
-#include <mlk/core/core.h>
-
-struct battle;
-struct character;
-struct texture;
-
-struct item {
-	const char *name;
-	const char *description;
-	struct texture *icon;
-
-	void (*exec_menu)(const struct item *, struct character *);
-
-	void (*exec_battle)(const struct item *,
-	                    struct battle *,
-	                    struct character *,
-	                    struct character *);
-};
-
-CORE_BEGIN_DECLS
-
-void
-item_exec_menu(const struct item *, struct character *);
-
-void
-item_exec_battle(const struct item *,
-                 struct battle *,
-                 struct character *,
-                 struct character *);
-
-CORE_END_DECLS
-
-#endif /* !MLK_RPG_ITEM_H */
--- a/src/libmlk-rpg/rpg/map-file.c	Sat Oct 15 21:19:25 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,312 +0,0 @@
-/*
- * map-file.c -- map file loader
- *
- * Copyright (c) 2020-2022 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 <errno.h>
-#include <limits.h>
-#include <stddef.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <mlk/util/util.h>
-
-#include <mlk/core/alloc.h>
-#include <mlk/core/error.h>
-#include <mlk/core/image.h>
-#include <mlk/core/trace.h>
-#include <mlk/core/util.h>
-
-#include "map-file.h"
-
-#define MAX_F(v) MAX_F_(v)
-#define MAX_F_(v) "%" #v "[^\n|]"
-
-struct context {
-	struct map_file *mf;            /* Map loader. */
-	struct map *map;                /* Map object to fill. */
-	FILE *fp;                       /* Map file pointer. */
-	char basedir[PATH_MAX];         /* Parent map directory */
-};
-
-static int
-parse_layer_tiles(struct context *ctx, const char *layer_name)
-{
-	enum map_layer_type layer_type;
-	size_t amount, current;
-
-	if (strcmp(layer_name, "background") == 0)
-		layer_type = MAP_LAYER_TYPE_BACKGROUND;
-	else if (strcmp(layer_name, "foreground") == 0)
-		layer_type = MAP_LAYER_TYPE_FOREGROUND;
-	else if (strcmp(layer_name, "above") == 0)
-		layer_type = MAP_LAYER_TYPE_ABOVE;
-	else
-		return errorf("invalid layer type: %s", layer_name);
-
-	amount = ctx->map->columns * ctx->map->rows;
-	current = 0;
-
-	/*
-	 * The next line after a layer declaration is a list of plain integer
-	 * that fill the layer tiles.
-	 */
-	if (!(ctx->mf->layers[layer_type].tiles = alloc_array0(amount, sizeof (unsigned short))))
-		return -1;
-
-	for (int tile; fscanf(ctx->fp, "%d\n", &tile) && current < amount; ++current)
-		ctx->mf->layers[layer_type].tiles[current] = tile;
-
-	ctx->map->layers[layer_type].tiles = ctx->mf->layers[layer_type].tiles;
-
-	return 0;
-}
-
-static int
-parse_actions(struct context *ctx)
-{
-	char exec[128 + 1];
-	int x = 0, y = 0, block = 0;
-	unsigned int w = 0, h = 0;
-
-	while (fscanf(ctx->fp, "%d|%d|%u|%u|%d|%128[^\n]\n", &x, &y, &w, &h, &block, exec) >= 5) {
-		struct map_block *reg;
-
-		if (!ctx->mf->load_action) {
-			tracef("ignoring action %d,%d,%u,%u,%d,%s", x, y, w, h, block, exec);
-			continue;
-		}
-
-		ctx->mf->load_action(ctx->map, x, y, w, h, exec);
-
-		/*
-		 * Actions do not have concept of collisions because they are
-		 * not only used on maps. The map structure has its very own
-		 * object to manage collisions but the .map file use the same
-		 * directive for simplicity. So create a block region if the
-		 * directive has one.
-		 */
-		if (block) {
-			if (!(reg = alloc_pool_new(&ctx->mf->blocks)))
-				return -1;
-
-			reg->x = x;
-			reg->y = y;
-			reg->w = w;
-			reg->h = h;
-		}
-	}
-
-	/* Reference the blocks array from map_file. */
-	ctx->map->blocks = ctx->mf->blocks.data;
-	ctx->map->blocksz = ctx->mf->blocks.size;
-
-	return 0;
-}
-
-static int
-parse_layer(struct context *ctx, const char *line)
-{
-	char layer_name[32 + 1] = {0};
-
-	/* Check if weight/height has been specified. */
-	if (ctx->map->columns == 0 || ctx->map->rows == 0)
-		return errorf("missing map dimensions before layer");
-
-	/* Determine layer type. */
-	if (sscanf(line, "layer|%32s", layer_name) <= 0)
-		return errorf("missing layer type definition");
-
-	if (strcmp(layer_name, "actions") == 0)
-		return parse_actions(ctx);
-
-	return parse_layer_tiles(ctx, layer_name);
-}
-
-static int
-parse_tileset(struct context *ctx, const char *line)
-{
-	char path[PATH_MAX] = {0}, *p;
-	struct map_file *mf = ctx->mf;
-	struct tileset_file *tf = &mf->tileset_file;
-
-	if (!(p = strchr(line, '|')))
-		return errorf("could not parse tileset");
-
-	snprintf(path, sizeof (path), "%s/%s", ctx->basedir, p + 1);
-
-	if (tileset_file_open(tf, &mf->tileset, path) < 0)
-		return -1;
-
-	ctx->map->tileset = &mf->tileset;
-
-	return 0;
-}
-
-static int
-parse_title(struct context *ctx, const char *line)
-{
-	if (sscanf(line, "title|" MAX_F(MAP_FILE_TITLE_MAX), ctx->mf->title) != 1 || strlen(ctx->mf->title) == 0)
-		return errorf("null map title");
-
-	ctx->map->title = ctx->mf->title;
-
-	return 0;
-}
-
-static int
-parse_columns(struct context *ctx, const char *line)
-{
-	if (sscanf(line, "columns|%u", &ctx->map->columns) != 1 || ctx->map->columns == 0)
-		return errorf("null map columns");
-
-	return 0;
-}
-
-static int
-parse_rows(struct context *ctx, const char *line)
-{
-	if (sscanf(line, "rows|%u", &ctx->map->rows) != 1 || ctx->map->rows == 0)
-		return errorf("null map rows");
-
-	return 0;
-}
-
-static int
-parse_origin(struct context *ctx, const char *line)
-{
-	if (sscanf(line, "origin|%d|%d", &ctx->map->player_x, &ctx->map->player_y) != 2)
-		return errorf("invalid origin");
-
-	return 0;
-}
-
-static int
-parse_line(struct context *ctx, const char *line)
-{
-	static const struct {
-		const char *property;
-		int (*read)(struct context *, const char *);
-	} props[] = {
-		{ "title",      parse_title             },
-		{ "columns",    parse_columns           },
-		{ "rows",       parse_rows              },
-		{ "tileset",    parse_tileset           },
-		{ "origin",     parse_origin            },
-		{ "layer",      parse_layer             },
-	};
-
-	for (size_t i = 0; i < UTIL_SIZE(props); ++i)
-		if (strncmp(line, props[i].property, strlen(props[i].property)) == 0)
-			return props[i].read(ctx, line);
-
-	return 0;
-}
-
-static int
-parse(struct context *ctx, const char *path)
-{
-	char line[1024];
-	char basedir[PATH_MAX];
-
-	util_strlcpy(basedir, path, sizeof (basedir));
-	util_strlcpy(ctx->basedir, util_dirname(basedir), sizeof (ctx->basedir));
-
-	while (fgets(line, sizeof (line), ctx->fp)) {
-		/* Remove \n if any */
-		line[strcspn(line, "\r\n")] = '\0';
-
-		if (parse_line(ctx, line) < 0)
-			return -1;
-	}
-
-	return 0;
-}
-
-static int
-check(struct map *map)
-{
-	/*
-	 * Check that we have parsed every required components.
-	 */
-	if (!map->title)
-		return errorf("missing title");
-
-	/*
-	 * We don't need to check width/height because parsing layers and
-	 * tilesets already check for their presence, so only check layers.
-	 */
-	if (!map->layers[0].tiles)
-		return errorf("missing background layer");
-	if (!map->layers[1].tiles)
-		return errorf("missing foreground layer");
-	if (!tileset_ok(map->tileset))
-		return errorf("missing tileset");
-
-	return 0;
-}
-
-int
-map_file_open(struct map_file *file, struct map *map, const char *path)
-{
-	assert(file);
-	assert(path);
-	assert(map);
-
-	struct context ctx = {
-		.mf = file,
-		.map = map,
-	};
-	int ret = 0;
-
-	memset(map, 0, sizeof (*map));
-	alloc_pool_init(&file->blocks, sizeof (*map->blocks), NULL);
-
-	if (!(ctx.fp = fopen(path, "r")))
-		goto fail;
-	if ((ret = parse(&ctx, path)) < 0 || (ret = check(map)) < 0)
-		goto fail;
-
-	fclose(ctx.fp);
-
-	return 0;
-
-fail:
-	map_finish(map);
-	map_file_finish(file);
-
-	if (ctx.fp)
-		fclose(ctx.fp);
-
-	return -1;
-}
-
-void
-map_file_finish(struct map_file *file)
-{
-	assert(file);
-
-	free(file->layers[0].tiles);
-	free(file->layers[1].tiles);
-	free(file->layers[2].tiles);
-
-	tileset_file_finish(&file->tileset_file);
-	alloc_pool_finish(&file->blocks);
-
-	memset(file, 0, sizeof (*file));
-}
--- a/src/libmlk-rpg/rpg/map-file.h	Sat Oct 15 21:19:25 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,53 +0,0 @@
-/*
- * map-file.h -- map file loader
- *
- * Copyright (c) 2020-2022 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 MLK_RPG_MAP_FILE_H
-#define MLK_RPG_MAP_FILE_H
-
-#include <mlk/core/alloc.h>
-#include <mlk/core/core.h>
-#include <mlk/core/sprite.h>
-#include <mlk/core/texture.h>
-
-#include "map.h"
-#include "tileset.h"
-#include "tileset-file.h"
-
-#define MAP_FILE_TITLE_MAX 64
-
-struct map_file {
-	void (*load_action)(struct map *, int, int, int, int, const char *);
-
-	char title[MAP_FILE_TITLE_MAX];
-	struct map_layer layers[MAP_LAYER_TYPE_NUM];
-	struct tileset_file tileset_file;
-	struct tileset tileset;
-	struct alloc_pool blocks;
-};
-
-CORE_BEGIN_DECLS
-
-int
-map_file_open(struct map_file *file, struct map *map, const char *path);
-
-void
-map_file_finish(struct map_file *file);
-
-CORE_END_DECLS
-
-#endif /* !MLK_RPG_MAP_FILE_H */
--- a/src/libmlk-rpg/rpg/map.c	Sat Oct 15 21:19:25 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,672 +0,0 @@
-/*
- * map.c -- game map
- *
- * Copyright (c) 2020-2022 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 <stdlib.h>
-#include <string.h>
-
-#include <mlk/core/error.h>
-#include <mlk/core/event.h>
-#include <mlk/core/image.h>
-#include <mlk/core/maths.h>
-#include <mlk/core/painter.h>
-#include <mlk/core/sprite.h>
-#include <mlk/core/sys.h>
-#include <mlk/core/texture.h>
-#include <mlk/core/window.h>
-
-#include <mlk/ui/debug.h>
-
-#include "map.h"
-#include "tileset.h"
-
-/*
- * This is the speed the player moves on the map.
- *
- * SPEED represents the number of pixels it must move per SEC.
- * SEC simply represends the number of milliseconds in one second.
- */
-#define SPEED 100
-#define SEC   1000
-
-/*
- * Those are margins within the edge of the screen. The camera always try to
- * keep those padding between the player and the screen.
- */
-#define MARGIN_WIDTH    160
-#define MARGIN_HEIGHT   90
-
-#define WIDTH(map)      ((map)->columns * (map)->tileset->sprite->cellw)
-#define HEIGHT(map)     ((map)->rows * (map)->tileset->sprite->cellh)
-
-/*
- * This structure defines the possible movement of the player as flags since
- * it's possible to make diagonal movements.
- */
-enum movement {
-	MOVING_UP       = 1 << 0,
-	MOVING_RIGHT    = 1 << 1,
-	MOVING_DOWN     = 1 << 2,
-	MOVING_LEFT     = 1 << 3
-};
-
-/*
- * A bit of explanation within this array. The structure walksprite requires
- * an orientation between 0-7 depending on the user direction.
- *
- * Since keys for moving the character may be pressed at the same time, we need
- * a conversion table from "key pressed" to "orientation".
- *
- * When an orientation is impossible, it is set to -1. Example, when both left
- * and right are pressed.
- *
- * MOVING_UP    = 0001 = 0x1
- * MOVING_RIGHT = 0010 = 0x2
- * MOVING_DOWN  = 0100 = 0x3
- * MOVING_LEFT  = 1000 = 0x4
- */
-static unsigned int orientations[16] = {
-	[0x1] = 0,
-	[0x2] = 2,
-	[0x3] = 1,
-	[0x4] = 4,
-	[0x6] = 3,
-	[0x8] = 6,
-	[0x9] = 7,
-	[0xC] = 5
-};
-
-/*
- * Check if this block is usable for collision detection. For example if the
- * player is moving upwards but the collision shape is below it is unnecessary
- * to check.
- */
-static int
-is_block_relevant(const struct map *map,
-                    const struct map_block *block,
-                    int drow,
-                    int dcol)
-{
-	if (drow) {
-		/* Object outside of left-right bounds. */
-		if (block->x + (int)block->w <= map->player_x ||
-		    block->x                 >= map->player_x + (int)map->player_sprite->cellw)
-			return 0;
-
-		if ((drow < 0 && block->y            >= map->player_y + (int)map->player_sprite->cellh) ||
-		    (drow > 0 && block->y + block->h <= map->player_y + map->player_sprite->cellh))
-			return 0;
-	} else if (dcol) {
-		/* Object outside of up-down bounds. */
-		if (block->y + (int)block->h <= map->player_y ||
-		    block->y                 >= map->player_y + (int)map->player_sprite->cellh)
-			return 0;
-
-		if ((dcol < 0 && block->x            >= map->player_x + (int)map->player_sprite->cellw) ||
-		    (dcol > 0 && block->x + block->w <= map->player_x + map->player_sprite->cellw))
-			return 0;
-	}
-
-	return 1;
-}
-
-/*
- * Determine if this collision shape is "closer" to the player by checking the
- * new block coordinates with the previous one.
- */
-static int
-is_block_better(const struct map_block *now,
-                const struct map_block *new,
-                int drow,
-                int dcol)
-{
-	return ((drow < 0 && new->y + new->h > now->y + now->h) ||
-	        (drow > 0 && new->y < now->y) ||
-	        (dcol < 0 && new->x + new->w > now->x + now->w) ||
-		(dcol > 0 && new->x < now->x));
-
-}
-
-static void
-center(struct map *map)
-{
-	map->view_x = map->player_x - (int)(map->view_w / 2);
-	map->view_y = map->player_y - (int)(map->view_h / 2);
-
-	if (map->view_x < 0)
-		map->view_x = 0;
-	else if ((unsigned int)map->view_x > WIDTH(map) - map->view_w)
-		map->view_x = WIDTH(map) - map->view_w;
-
-	if (map->view_y < 0)
-		map->view_y = 0;
-	else if ((unsigned int)map->view_y > HEIGHT(map) - map->view_h)
-		map->view_y = HEIGHT(map) - map->view_h;
-}
-
-static void
-init(struct map *map)
-{
-	/* Adjust view. */
-	map->view_w = window.w;
-	map->view_h = window.h;
-
-	/* Adjust margin. */
-	map->margin_w = map->view_w - (MARGIN_WIDTH * 2) - map->player_sprite->cellw;
-	map->margin_h = map->view_h - (MARGIN_HEIGHT * 2) - map->player_sprite->cellh;
-
-	/* Center the view by default. */
-	center(map);
-
-	/* Final bits. */
-	walksprite_init(&map->player_ws, map->player_sprite, 150);
-}
-
-static void
-handle_keydown(struct map *map, const union event *event)
-{
-	switch (event->key.key) {
-	case KEY_UP:
-		map->player_movement |= MOVING_UP;
-		break;
-	case KEY_RIGHT:
-		map->player_movement |= MOVING_RIGHT;
-		break;
-	case KEY_DOWN:
-		map->player_movement |= MOVING_DOWN;
-		break;
-	case KEY_LEFT:
-		map->player_movement |= MOVING_LEFT;
-		break;
-	default:
-		break;
-	}
-
-	map->player_angle = orientations[map->player_movement];
-}
-
-static void
-handle_keyup(struct map *map, const union event *event)
-{
-	switch (event->key.key) {
-	case KEY_TAB:
-		map->flags ^= MAP_FLAGS_SHOW_GRID | MAP_FLAGS_SHOW_COLLIDE;
-		break;
-	case KEY_UP:
-		map->player_movement &= ~(MOVING_UP);
-		break;
-	case KEY_RIGHT:
-		map->player_movement &= ~(MOVING_RIGHT);
-		break;
-	case KEY_DOWN:
-		map->player_movement &= ~(MOVING_DOWN);
-		break;
-	case KEY_LEFT:
-		map->player_movement &= ~(MOVING_LEFT);
-		break;
-	default:
-		break;
-	}
-}
-
-static int
-cmp_tile(const struct tileset_tiledef *td1, const struct tileset_tiledef *td2)
-{
-	if (td1->id < td2->id)
-		return -1;
-	if (td1->id > td2->id)
-		return 1;
-
-	return 0;
-}
-
-static struct tileset_tiledef *
-find_tiledef_by_id(const struct map *map, unsigned short id)
-{
-	typedef int (*cmp)(const void *, const void *);
-
-	const struct tileset_tiledef key = {
-		.id = id
-	};
-
-	return bsearch(&key, map->tileset->tiledefs, map->tileset->tiledefsz,
-	    sizeof (key), (cmp)cmp_tile);
-}
-
-static struct tileset_tiledef *
-find_tiledef_by_row_column_in_layer(const struct map *map,
-                                    const struct map_layer *layer,
-                                    int row,
-                                    int col)
-{
-	unsigned short id;
-
-	if (row < 0 || (unsigned int)row >= map->rows ||
-	    col < 0 || (unsigned int)col >= map->columns)
-		return 0;
-
-	if ((id = layer->tiles[col + row * map->columns]) == 0)
-		return NULL;
-
-	return find_tiledef_by_id(map, id - 1);
-}
-
-static struct tileset_tiledef *
-find_tiledef_by_row_column(const struct map *map, int row, int col)
-{
-	struct tileset_tiledef *tile;
-
-	/* TODO: probably a for loop when we have indefinite layers. */
-	if (!(tile = find_tiledef_by_row_column_in_layer(map, &map->layers[1], row, col)))
-		tile = find_tiledef_by_row_column_in_layer(map, &map->layers[0], row, col);
-
-	return tile;
-}
-
-static void
-find_block_iterate(const struct map *map,
-                   struct map_block *block,
-                   int rowstart,
-                   int rowend,
-                   int colstart,
-                   int colend,
-                   int drow,
-                   int dcol)
-{
-	assert(map);
-	assert(block);
-
-	/* First, check with tiledefs. */
-	for (int r = rowstart; r <= rowend; ++r) {
-		for (int c = colstart; c <= colend; ++c) {
-			struct tileset_tiledef *td;
-			struct map_block tmp;
-
-			if (!(td = find_tiledef_by_row_column(map, r, c)))
-				continue;
-
-			/* Convert to absolute values. */
-			tmp.x = td->x + c * map->tileset->sprite->cellw;
-			tmp.y = td->y + r * map->tileset->sprite->cellh;
-			tmp.w = td->w;
-			tmp.h = td->h;
-
-			/* This tiledef is out of context. */
-			if (!is_block_relevant(map, &tmp, drow, dcol))
-				continue;
-
-			if (is_block_better(block, &tmp, drow, dcol)) {
-				block->x = tmp.x;
-				block->y = tmp.y;
-				block->w = tmp.w;
-				block->h = tmp.h;
-			}
-		}
-	}
-
-	/* Now check if there are objects closer than tiledefs. */
-	for (size_t i = 0; i < map->blocksz; ++i) {
-		const struct map_block *new = &map->blocks[i];
-
-		if (is_block_relevant(map, new, drow, dcol) &&
-		    is_block_better(block, new, drow, dcol)) {
-			block->x = new->x;
-			block->y = new->y;
-			block->w = new->w;
-			block->h = new->h;
-		}
-	}
-}
-
-static void
-find_collision(const struct map *map, struct map_block *block, int drow, int dcolumn)
-{
-	assert((drow && !dcolumn) || (dcolumn && !drow));
-
-	const int playercol = map->player_x / map->tileset->sprite->cellw;
-	const int playerrow = map->player_y / map->tileset->sprite->cellh;
-	const int ncols = (map->player_sprite->cellw / map->tileset->sprite->cellw) + 1;
-	const int nrows = (map->player_sprite->cellh / map->tileset->sprite->cellh) + 1;
-	int rowstart, rowend, colstart, colend;
-
-	if (drow) {
-		colstart = playercol;
-		colend = playercol + ncols;
-
-		if (drow < 0) {
-			/* Moving UP. */
-			rowstart = 0;
-			rowend = playerrow;
-			block->x = block->y = block->h = 0;
-			block->w = WIDTH(map);
-		} else {
-			/* Moving DOWN. */
-			rowstart = playerrow;
-			rowend = HEIGHT(map);
-			block->x = block->h = 0;
-			block->y = HEIGHT(map);
-			block->w = WIDTH(map);
-		}
-	} else {
-		rowstart = playerrow;
-		rowend = playerrow + nrows;
-
-		if (dcolumn < 0) {
-			/* Moving LEFT. */
-			colstart = 0;
-			colend = playercol;
-			block->x = block->y = block->w = 0;
-			block->h = HEIGHT(map);
-		} else {
-			/* Moving RIGHT. */
-			colstart = playercol;
-			colend = WIDTH(map);
-			block->x = WIDTH(map);
-			block->y = block->w = 0;
-			block->h = block->h;
-		}
-	}
-
-	find_block_iterate(map, block, rowstart, rowend, colstart, colend, drow, dcolumn);
-}
-
-static void
-move_x(struct map *map, int delta)
-{
-	struct map_block block;
-
-	find_collision(map, &block, 0, delta < 0 ? -1 : +1);
-
-	if (delta < 0 && map->player_x + delta < (int)(block.x + block.w))
-		delta = map->player_x - block.x - block.w;
-	else if (delta > 0 && (int)(map->player_x + map->player_sprite->cellw + delta) >= block.x)
-		delta = block.x - map->player_x - (int)(map->player_sprite->cellw);
-
-	map->player_x += delta;
-
-	if ((delta < 0 && map->player_x < map->margin_x) ||
-	    (delta > 0 && map->player_x >= (int)(map->margin_x + map->margin_w)))
-		map->view_x += delta;
-
-	if (map->view_x < 0)
-		map->view_x = 0;
-	else if (map->view_x >= (int)(WIDTH(map) - map->view_w))
-		map->view_x = WIDTH(map) - map->view_w;
-}
-
-static void
-move_y(struct map *map, int delta)
-{
-	struct map_block block;
-
-	find_collision(map, &block, delta < 0 ? -1 : +1, 0);
-
-	if (delta < 0 && map->player_y + delta < (int)(block.y + block.h))
-		delta = map->player_y - block.y - block.h;
-	else if (delta > 0 && (int)(map->player_y + map->player_sprite->cellh + delta) >= block.y)
-		delta = block.y - map->player_y - (int)(map->player_sprite->cellh);
-
-	map->player_y += delta;
-
-	if ((delta < 0 && map->player_y < map->margin_y) ||
-	    (delta > 0 && map->player_y >= (int)(map->margin_y + map->margin_h)))
-		map->view_y += delta;
-
-	if (map->view_y < 0)
-		map->view_y = 0;
-	else if (map->view_y >= (int)(HEIGHT(map) - map->view_h))
-		map->view_y = HEIGHT(map) - map->view_h;
-}
-
-static void
-move(struct map *map, unsigned int ticks)
-{
-	/* This is the amount of pixels the player must move. */
-	const int delta = SPEED * ticks / SEC;
-
-	/* This is the rectangle within the view where users must be. */
-	map->margin_x = map->view_x + MARGIN_WIDTH;
-	map->margin_y = map->view_y + MARGIN_HEIGHT;
-
-	int dx = 0;
-	int dy = 0;
-
-	if (map->player_movement == 0)
-		return;
-
-	if (map->player_movement & MOVING_UP)
-		dy = -1;
-	if (map->player_movement & MOVING_DOWN)
-		dy = 1;
-	if (map->player_movement & MOVING_LEFT)
-		dx = -1;
-	if (map->player_movement & MOVING_RIGHT)
-		dx = 1;
-
-	/* Move the player and adjust view if needed. */
-	if (dx)
-		move_x(map, dx * delta);
-	if (dy)
-		move_y(map, dy * delta);
-
-	walksprite_update(&map->player_ws, ticks);
-}
-
-static inline void
-draw_layer_tile(const struct map *map,
-                const struct map_layer *layer,
-                struct texture *colbox,
-                int start_col,
-                int start_row,
-                int start_x,
-                int start_y,
-                unsigned int r,
-                unsigned int c)
-{
-	const struct tileset_tiledef *td;
-	int index, id, sc, sr, mx, my;
-
-	index = (start_col + c) + ((start_row + r) * map->columns);
-
-	if ((id = layer->tiles[index]) == 0)
-		return;
-
-	id -= 1;
-
-	/* Sprite row/column. */
-	sc = (id) % map->tileset->sprite->ncols;
-	sr = (id) / map->tileset->sprite->ncols;
-
-	/* On screen coordinates. */
-	mx = start_x + (int)c * (int)map->tileset->sprite->cellw;
-	my = start_y + (int)r * (int)map->tileset->sprite->cellh;
-
-	tileset_draw(map->tileset, sr, sc, mx, my);
-
-	if ((td = find_tiledef_by_id(map, id)) && texture_ok(colbox))
-		texture_scale(colbox, 0, 0, 5, 5, mx + td->x, my + td->y, td->w, td->h, 0);
-
-	if (map->flags & MAP_FLAGS_SHOW_GRID) {
-		painter_set_color(0x202e37ff);
-		painter_draw_line(mx, my, mx + (int)map->tileset->sprite->cellw, my);
-		painter_draw_line(
-		    mx + (int)map->tileset->sprite->cellw - 1, my,
-		    mx + (int)map->tileset->sprite->cellw - 1, my + (int)map->tileset->sprite->cellh);
-	}
-}
-
-static void
-draw_layer(const struct map *map, const struct map_layer *layer)
-{
-	assert(map);
-	assert(layer);
-
-	/* Beginning of view in row/column. */
-	const unsigned int start_col = map->view_x / map->tileset->sprite->cellw;
-	const unsigned int start_row = map->view_y / map->tileset->sprite->cellh;
-
-	/* Convert into x/y coordinate. */
-	const int start_x = 0 - (map->view_x % (int)map->tileset->sprite->cellw);
-	const int start_y = 0 - (map->view_y % (int)map->tileset->sprite->cellh);
-
-	/* Number of row/columns to draw starting from there. */
-	const unsigned int ncols = (map->view_w / map->tileset->sprite->cellw) + 2;
-	const unsigned int nrows = (map->view_h / map->tileset->sprite->cellh) + 2;
-
-	struct texture colbox = {0};
-
-	if (!layer->tiles)
-		return;
-
-	/* Show collision box if requested. */
-	if (map->flags & MAP_FLAGS_SHOW_COLLIDE && texture_new(&colbox, 16, 16) == 0) {
-		texture_set_blend_mode(&colbox, TEXTURE_BLEND_BLEND);
-		texture_set_alpha_mod(&colbox, 100);
-		PAINTER_BEGIN(&colbox);
-		painter_set_color(0xa53030ff);
-		painter_clear();
-		PAINTER_END();
-	}
-
-	for (unsigned int r = 0; r < nrows; ++r) {
-		for (unsigned int c = 0; c < ncols; ++c) {
-			if (start_col + c >= map->columns ||
-			    start_row + r >= map->rows)
-				continue;
-
-			draw_layer_tile(map, layer, &colbox, start_col, start_row, start_x, start_y, r, c);
-		}
-	}
-
-	texture_finish(&colbox);
-}
-
-static void
-draw_collide(const struct map *map)
-{
-	struct texture box = {0};
-
-	if (map->flags & MAP_FLAGS_SHOW_COLLIDE && texture_new(&box, 64, 64) == 0) {
-		/* Draw collide box around player if requested. */
-		texture_set_alpha_mod(&box, 100);
-		texture_set_blend_mode(&box, TEXTURE_BLEND_BLEND);
-		PAINTER_BEGIN(&box);
-		painter_set_color(0x4f8fbaff);
-		painter_clear();
-		PAINTER_END();
-		texture_scale(&box, 0, 0, 64, 64,
-		    map->player_x - map->view_x, map->player_y - map->view_y,
-			      map->player_sprite->cellw, map->player_sprite->cellh, 0.f);
-
-		/* Do the same for every objects. */
-		PAINTER_BEGIN(&box);
-		painter_set_color(0xa8ca58ff);
-		painter_clear();
-		PAINTER_END();
-
-		for (size_t i = 0; i < map->blocksz; ++i) {
-			texture_scale(&box, 0, 0, 64, 64,
-			    map->blocks[i].x - map->view_x, map->blocks[i].y - map->view_y,
-			    map->blocks[i].w, map->blocks[i].h,
-			    0.f);
-		}
-
-		texture_finish(&box);
-	}
-}
-
-int
-map_init(struct map *map)
-{
-	assert(map);
-
-	init(map);
-	tileset_start(map->tileset);
-
-	return 0;
-}
-
-void
-map_handle(struct map *map, const union event *ev)
-{
-	assert(map);
-	assert(ev);
-
-	switch (ev->type) {
-	case EVENT_KEYDOWN:
-		handle_keydown(map, ev);
-		break;
-	case EVENT_KEYUP:
-		handle_keyup(map, ev);
-		break;
-	default:
-		break;
-	}
-
-	action_stack_handle(&map->astack_par, ev);
-	action_stack_handle(&map->astack_seq, ev);
-}
-
-void
-map_update(struct map *map, unsigned int ticks)
-{
-	assert(map);
-
-	action_stack_update(&map->astack_par, ticks);
-	action_stack_update(&map->astack_seq, ticks);
-
-	tileset_update(map->tileset, ticks);
-
-	/* No movements if the sequential actions are running. */
-	if (action_stack_completed(&map->astack_seq))
-		move(map, ticks);
-}
-
-void
-map_draw(const struct map *map)
-{
-	assert(map);
-
-	/* Draw the texture about background/foreground. */
-	draw_layer(map, &map->layers[MAP_LAYER_TYPE_BACKGROUND]);
-	draw_layer(map, &map->layers[MAP_LAYER_TYPE_FOREGROUND]);
-
-	walksprite_draw(
-		&map->player_ws,
-		map->player_angle,
-		map->player_x - map->view_x,
-		map->player_y - map->view_y);
-
-	draw_layer(map, &map->layers[MAP_LAYER_TYPE_ABOVE]);
-	draw_collide(map);
-
-	action_stack_draw(&map->astack_par);
-	action_stack_draw(&map->astack_seq);
-}
-
-void
-map_finish(struct map *map)
-{
-	assert(map);
-
-	action_stack_finish(&map->astack_par);
-	action_stack_finish(&map->astack_seq);
-
-	memset(map, 0, sizeof (*map));
-}
--- a/src/libmlk-rpg/rpg/map.h	Sat Oct 15 21:19:25 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,120 +0,0 @@
-/*
- * map.h -- game map
- *
- * Copyright (c) 2020-2022 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 MLK_RPG_MAP_H
-#define MLK_RPG_MAP_H
-
-#include <stddef.h>
-
-#include <mlk/core/action.h>
-#include <mlk/core/action-stack.h>
-#include <mlk/core/core.h>
-
-#include "walksprite.h"
-
-struct tileset;
-
-union event;
-
-enum map_layer_type {
-	MAP_LAYER_TYPE_BACKGROUND,
-	MAP_LAYER_TYPE_FOREGROUND,
-	MAP_LAYER_TYPE_ABOVE,
-	MAP_LAYER_TYPE_NUM
-};
-
-struct map_layer {
-	unsigned short *tiles;
-};
-
-enum map_flags {
-	MAP_FLAGS_NONE          = 0,
-	MAP_FLAGS_SHOW_GRID     = (1 << 0),
-	MAP_FLAGS_SHOW_COLLIDE  = (1 << 2)
-};
-
-struct map_block {
-	int x;
-	int y;
-	unsigned int w;
-	unsigned int h;
-};
-
-struct map {
-	const char *title;              /*!< (+) Map title name. */
-	unsigned int columns;           /*!< (-) Number of columns. */
-	unsigned int rows;              /*!< (-) Number of rows. */
-
-	/* Tileset. */
-	struct tileset *tileset;        /*!< (+&?) Tileset to use. */
-
-	/* View options. */
-	enum map_flags flags;           /*!< (+) View options. */
-
-	/* Extra collisions blocks. */
-	struct map_block *blocks;       /*!< (+&?) Extra collisions. */
-	size_t blocksz;                 /*!< (+) Number of collisions. */
-
-	/* List of actions. */
-	struct action_stack astack_par; /*!< (+) Parallel actions. */
-	struct action_stack astack_seq; /*!< (+) Blocking actions. */
-
-	/* Player. */
-	struct sprite *player_sprite;   /*!< (+) The sprite to use */
-	struct walksprite player_ws;    /*!< (-) Walking sprite for moving the player. */
-	int player_x;                   /*!< (+) Player position in x */
-	int player_y;                   /*!< (+) Player position in y */
-	int player_angle;               /*!< (+) Player angle (see walksprite) */
-	unsigned int player_movement;   /*!< (*) Current player movements. */
-
-	/* View to zoom/locate. */
-	int view_x;                     /*!< (+) Position in x */
-	int view_y;                     /*!< (+) Position in y */
-	unsigned int view_w;            /*!< (+) View width */
-	unsigned int view_h;            /*!< (+) View height */
-
-	/* View margin. */
-	int margin_x;                   /*!< (+) View margin in x. */
-	int margin_y;                   /*!< (+) View margin in y. */
-	unsigned int margin_w;          /*!< (+) Margin width. */
-	unsigned int margin_h;          /*!< (+) Margin height. */
-
-	/* Different tile layers. */
-	struct map_layer layers[MAP_LAYER_TYPE_NUM];
-};
-
-CORE_BEGIN_DECLS
-
-int
-map_init(struct map *map);
-
-void
-map_handle(struct map *map, const union event *ev);
-
-void
-map_update(struct map *map, unsigned int ticks);
-
-void
-map_draw(const struct map *map);
-
-void
-map_finish(struct map *map);
-
-CORE_END_DECLS
-
-#endif /* !MLK_RPG_MAP_H */
--- a/src/libmlk-rpg/rpg/message.c	Sat Oct 15 21:19:25 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,316 +0,0 @@
-/*
- * message.c -- message dialog
- *
- * Copyright (c) 2020-2022 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 <stdlib.h>
-#include <string.h>
-
-#include <mlk/core/action.h>
-#include <mlk/core/event.h>
-#include <mlk/core/font.h>
-#include <mlk/core/maths.h>
-#include <mlk/core/painter.h>
-#include <mlk/core/panic.h>
-#include <mlk/core/sprite.h>
-#include <mlk/core/trace.h>
-#include <mlk/core/util.h>
-
-#include <mlk/ui/align.h>
-#include <mlk/ui/frame.h>
-#include <mlk/ui/label.h>
-#include <mlk/ui/theme.h>
-
-#include "message.h"
-
-#define THEME(msg)      (msg->theme ? msg->theme : theme_default())
-
-static void
-handle(struct action *action, const union event *ev)
-{
-	assert(action);
-	assert(ev);
-
-	message_handle(action->data, ev);
-}
-
-static int
-update(struct action *action, unsigned int ticks)
-{
-	assert(action);
-
-	return message_update(action->data, ticks);
-}
-
-static void
-draw(struct action *action)
-{
-	assert(action);
-
-	message_draw(action->data);
-}
-
-static void
-draw_frame(const struct message *msg)
-{
-	assert(msg);
-
-	struct frame frame = {
-		.w = msg->w,
-		.h = msg->h,
-		.theme = msg->theme
-	};
-
-	frame_draw(&frame);
-}
-
-static inline unsigned int
-min_width(const struct message *msg)
-{
-	assert(msg);
-
-	unsigned int maxw = 0, w = 0;
-
-	for (size_t i = 0; i < msg->linesz; ++i) {
-		if (!msg->lines[i])
-			continue;
-		if (font_query(THEME(msg)->fonts[THEME_FONT_INTERFACE], msg->lines[i], &w, NULL) < 0)
-			panic();
-		if (w > maxw)
-			maxw = w;
-	}
-
-	return (THEME(msg)->padding * 2) + maxw;
-}
-
-static inline unsigned int
-min_height(const struct message *msg)
-{
-	assert(msg);
-
-	const struct theme *th = THEME(msg);
-	const unsigned int lh  = font_height(th->fonts[THEME_FONT_INTERFACE]);
-
-	return (th->padding * 2) + (msg->linesz * lh) + ((msg->linesz - 1) * msg->spacing);
-}
-
-static void
-draw_lines(const struct message *msg)
-{
-	const struct theme *theme = THEME(msg);
-	struct label label;
-	unsigned int lw, lh;
-
-	for (size_t i = 0; i < msg->linesz; ++i) {
-		if (!msg->lines[i])
-			continue;
-		if (font_query(theme->fonts[THEME_FONT_INTERFACE], msg->lines[i], &lw, &lh) < 0)
-			panic();
-
-		label.theme = theme;
-		label.x = theme->padding;
-		label.y = theme->padding + (i * (lh + msg->spacing));
-		label.text = msg->lines[i];
-		label.flags = LABEL_FLAGS_SHADOW;
-
-		if (label.x + lw > msg->w)
-			tracef("message width too small: %u < %u", msg->w, min_width(msg));
-		if (label.y + lh > msg->h)
-			tracef("message height too small: %u < %u", msg->h, min_height(msg));
-
-		/*
-		 * The function label_draw will use THEME_COLOR_NORMAL to draw
-		 * text and THEME_COLOR_SHADOW so if we have selected a line
-		 * we need to cheat the normal color.
-		 */
-		if (msg->flags & MESSAGE_FLAGS_QUESTION && msg->index == (unsigned int)i)
-			label.flags |= LABEL_FLAGS_SELECTED;
-		else
-			label.flags &= ~(LABEL_FLAGS_SELECTED);
-
-		label_draw(&label);
-	}
-}
-
-void
-message_start(struct message *msg)
-{
-	assert(msg);
-
-	if (msg->flags & (MESSAGE_FLAGS_FADEIN|MESSAGE_FLAGS_FADEOUT))
-		assert(msg->delay > 0);
-
-	msg->elapsed = 0;
-	msg->scale = msg->flags & MESSAGE_FLAGS_FADEIN ? 0.0 : 1.0;
-	msg->state = msg->flags & MESSAGE_FLAGS_FADEIN
-	    ? MESSAGE_STATE_OPENING
-	    : MESSAGE_STATE_SHOWING;
-
-	if (msg->flags & MESSAGE_FLAGS_AUTOMATIC && msg->timeout == 0)
-		tracef("message is automatic but has zero timeout");
-}
-
-void
-message_query(const struct message *msg, unsigned int *w, unsigned int *h)
-{
-	assert(msg);
-
-	if (w)
-		*w = min_width(msg);
-	if (h)
-		*h = min_height(msg);
-}
-
-void
-message_handle(struct message *msg, const union event *ev)
-{
-	assert(msg);
-	assert(ev);
-
-	/* Skip if the message animation hasn't complete. */
-	if (msg->state != MESSAGE_STATE_SHOWING)
-		return;
-
-	/* Only keyboard event are valid. */
-	if (ev->type != EVENT_KEYDOWN || msg->state == MESSAGE_STATE_NONE)
-		return;
-
-	switch (ev->key.key) {
-	case KEY_UP:
-		if (msg->index > 0)
-			msg->index--;
-		break;
-	case KEY_DOWN:
-		if (msg->index + 1 < msg->linesz && msg->lines[msg->index + 1])
-			msg->index++;
-		break;
-	case KEY_ENTER:
-		msg->state = msg->flags & MESSAGE_FLAGS_FADEOUT
-		    ? MESSAGE_STATE_HIDING
-		    : MESSAGE_STATE_NONE;
-		msg->elapsed = 0;
-		break;
-	default:
-		break;
-	}
-}
-
-int
-message_update(struct message *msg, unsigned int ticks)
-{
-	assert(msg);
-
-	msg->elapsed += ticks;
-
-	switch (msg->state) {
-	case MESSAGE_STATE_OPENING:
-		msg->scale = (double)msg->elapsed / (double)msg->delay;
-
-		if (msg->scale > 1)
-			msg->scale = 1;
-
-		if (msg->elapsed >= msg->delay) {
-			msg->state = MESSAGE_STATE_SHOWING;
-			msg->elapsed = 0;
-		}
-
-		break;
-	case MESSAGE_STATE_SHOWING:
-		/* Do automatically switch state if requested by the user. */
-		if (msg->flags & MESSAGE_FLAGS_AUTOMATIC && msg->elapsed >= msg->timeout) {
-			msg->state = msg->flags & MESSAGE_FLAGS_FADEOUT
-			    ? MESSAGE_STATE_HIDING
-			    : MESSAGE_STATE_NONE;
-			msg->elapsed = 0;
-		}
-
-		break;
-	case MESSAGE_STATE_HIDING:
-		msg->scale = 1 - (double)msg->elapsed / (double)msg->delay;
-
-		if (msg->scale < 0)
-			msg->scale = 0;
-		if (msg->elapsed >= msg->delay) {
-			msg->state = MESSAGE_STATE_NONE;
-			msg->elapsed = 0;
-		}
-
-		break;
-	default:
-		break;
-	}
-
-	return msg->state == MESSAGE_STATE_NONE;
-}
-
-void
-message_draw(const struct message *msg)
-{
-	assert(msg);
-
-	struct texture tex;
-	int x, y;
-	unsigned int w, h;
-
-	if (msg->w == 0 || msg->h == 0) {
-		tracef("message has null dimensions");
-		return;
-	}
-
-	if (texture_new(&tex, msg->w, msg->h) < 0)
-		panic();
-
-	PAINTER_BEGIN(&tex);
-	draw_frame(msg);
-	draw_lines(msg);
-	PAINTER_END();
-
-	/* Compute scaling. */
-	w = msg->w * msg->scale;
-	h = msg->h * msg->scale;
-
-	/* Centerize within its drawing area. */
-	align(ALIGN_CENTER, &x, &y, w, h, msg->x, msg->y, msg->w, msg->h);
-
-	/* Draw and clear. */
-	texture_scale(&tex, 0, 0, msg->w, msg->h, x, y, w, h, 0);
-	texture_finish(&tex);
-}
-
-void
-message_hide(struct message *msg)
-{
-	assert(msg);
-
-	msg->state = MESSAGE_STATE_HIDING;
-	msg->elapsed = 0;
-}
-
-void
-message_action(struct message *msg, struct action *action)
-{
-	assert(msg);
-	assert(action);
-
-	memset(action, 0, sizeof (struct action));
-	action->data = msg;
-	action->handle = handle;
-	action->update = update;
-	action->draw = draw;
-
-	message_start(msg);
-}
--- a/src/libmlk-rpg/rpg/message.h	Sat Oct 15 21:19:25 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,91 +0,0 @@
-/*
- * message.h -- message dialog
- *
- * Copyright (c) 2020-2022 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 MLK_RPG_MESSAGE_H
-#define MLK_RPG_MESSAGE_H
-
-#include <mlk/core/core.h>
-#include <mlk/core/texture.h>
-
-struct action;
-struct font;
-struct theme;
-
-union event;
-
-#define MESSAGE_DELAY_DEFAULT           (150)
-#define MESSAGE_TIMEOUT_DEFAULT         (5000)
-
-enum message_flags {
-	MESSAGE_FLAGS_AUTOMATIC         = (1 << 0),
-	MESSAGE_FLAGS_QUESTION          = (1 << 1),
-	MESSAGE_FLAGS_FADEIN            = (1 << 2),
-	MESSAGE_FLAGS_FADEOUT           = (1 << 3)
-};
-
-enum message_state {
-	MESSAGE_STATE_NONE,
-	MESSAGE_STATE_OPENING,
-	MESSAGE_STATE_SHOWING,
-	MESSAGE_STATE_HIDING
-};
-
-struct message {
-	int x;
-	int y;
-	unsigned int w;
-	unsigned int h;
-	unsigned int spacing;
-	unsigned int delay;
-	unsigned int timeout;
-	const char * const *lines;
-	size_t linesz;
-	unsigned int index;
-	enum message_flags flags;
-	enum message_state state;
-	const struct theme *theme;
-	unsigned int elapsed;
-	double scale;
-};
-
-CORE_BEGIN_DECLS
-
-void
-message_start(struct message *msg);
-
-void
-message_query(const struct message *msg, unsigned int *w, unsigned int *h);
-
-void
-message_handle(struct message *msg, const union event *ev);
-
-int
-message_update(struct message *msg, unsigned int ticks);
-
-void
-message_draw(const struct message *msg);
-
-void
-message_hide(struct message *msg);
-
-void
-message_action(struct message *msg, struct action *act);
-
-CORE_END_DECLS
-
-#endif /* !MLK_RPG_MESSAGE_H */
--- a/src/libmlk-rpg/rpg/property.c	Sat Oct 15 21:19:25 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,63 +0,0 @@
-/*
- * property.c -- manage game properties
- *
- * Copyright (c) 2020-2022 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/property-save.h>
-#include <assets/sql/property-remove.h>
-#include <assets/sql/property-load.h>
-
-#include "property.h"
-#include "save.h"
-
-int
-property_save(const struct property *p, struct save *s)
-{
-	assert(p);
-	assert(save_ok(s));
-
-	return save_exec(s, (const char *)assets_property_save, "ss", p->key, p->value);
-}
-
-int
-property_load(struct property *p, struct save *s)
-{
-	assert(p);
-	assert(save_ok(s));
-
-	struct save_stmt stmt;
-	enum save_stmt_errno ret;
-
-	if (save_stmt_init(&stmt, s, (const char *)assets_property_load, "s", p->key) < 0)
-		return -1;
-
-	ret = save_stmt_next(&stmt, "s", p->value, sizeof (p->value)) == SAVE_STMT_ROW;
-	save_stmt_finish(&stmt);
-
-	return ret ? 0 : -1;
-}
-
-int
-property_remove(struct property *p, struct save *s)
-{
-	assert(p);
-	assert(save_ok(s));
-
-	return save_exec(s, (const char *)assets_property_remove, "s", p->key);
-}
-
--- a/src/libmlk-rpg/rpg/property.h	Sat Oct 15 21:19:25 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,47 +0,0 @@
-/*
- * property.h -- manage game properties
- *
- * Copyright (c) 2020-2022 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 MLK_RPG_PROPERTY_H
-#define MLK_RPG_PROPERTY_H
-
-#include <mlk/core/core.h>
-
-#define PROPERTY_KEY_MAX        (64)
-#define PROPERTY_VALUE_MAX      (1024)
-
-struct save;
-
-struct property {
-	char key[PROPERTY_KEY_MAX + 1];
-	char value[PROPERTY_VALUE_MAX + 1];
-};
-
-CORE_BEGIN_DECLS
-
-int
-property_save(const struct property *, struct save *);
-
-int
-property_load(struct property *, struct save *);
-
-int
-property_remove(struct property *, struct save *);
-
-CORE_END_DECLS
-
-#endif /* !MLK_RPG_PROPERTY_H */
--- a/src/libmlk-rpg/rpg/quest.c	Sat Oct 15 21:19:25 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,83 +0,0 @@
-/*
- * quest.c -- in game quests
- *
- * Copyright (c) 2020-2022 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_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_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_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;
-}
--- a/src/libmlk-rpg/rpg/quest.h	Sat Oct 15 21:19:25 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,51 +0,0 @@
-/*
- * quest.h -- in game quests
- *
- * Copyright (c) 2020-2022 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 MLK_RPG_QUEST_H
-#define MLK_RPG_QUEST_H
-
-#include <stddef.h>
-
-#include <mlk/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 /* !MLK_RPG_QUEST_H */
--- a/src/libmlk-rpg/rpg/rpg.c	Sat Oct 15 21:19:25 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,30 +0,0 @@
-/*
- * rpg.c -- librpg convenient header
- *
- * Copyright (c) 2020-2022 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 "rpg.h"
-
-int
-rpg_init(void)
-{
-	return 0;
-}
-
-void
-rpg_finish(void)
-{
-}
--- a/src/libmlk-rpg/rpg/rpg.h	Sat Oct 15 21:19:25 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,34 +0,0 @@
-/*
- * rpg.h -- librpg convenient header
- *
- * Copyright (c) 2020-2022 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 MLK_RPG_RPG_H
-#define MLK_RPG_RPG_H
-
-#include <mlk/core/core.h>
-
-CORE_BEGIN_DECLS
-
-int
-rpg_init(void);
-
-void
-rpg_finish(void);
-
-CORE_END_DECLS
-
-#endif /* !MLK_RPG_H */
--- a/src/libmlk-rpg/rpg/save.c	Sat Oct 15 21:19:25 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,318 +0,0 @@
-/*
- * save.c -- save functions
- *
- * Copyright (c) 2020-2022 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 <stdlib.h>
-#include <string.h>
-
-#include <sqlite3.h>
-
-#include <mlk/util/util.h>
-
-#include <mlk/core/error.h>
-#include <mlk/core/sys.h>
-#include <mlk/core/util.h>
-
-#include <assets/sql/init.h>
-
-#include "property.h"
-#include "save.h"
-
-#define SQL_BEGIN       "BEGIN EXCLUSIVE TRANSACTION"
-#define SQL_COMMIT      "COMMIT"
-#define SQL_ROLLBACK    "ROLLBACK"
-
-static int
-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 0;
-}
-
-static const char *
-path(unsigned int idx)
-{
-	return util_pathf("%s%u.db", sys_dir(SYS_DIR_SAVE), idx);
-}
-
-static int
-execu(struct save *db, const unsigned char *sql)
-{
-	return exec(db, (const char *)sql);
-}
-
-static int
-verify(struct save *db)
-{
-	struct {
-		time_t *date;
-		struct 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 (property_load(&table[i].prop, db) < 0) {
-			sqlite3_close(db->handle);
-			return errorf("database not initialized correctly");
-		}
-
-		*table[i].date = strtoull(table[i].prop.value, NULL, 10);
-	}
-
-	return 0;
-}
-
-static int
-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 -1;
-			break;
-		case 's':
-			if (sqlite3_bind_text(stmt->handle, i++, va_arg(ap, const char *), -1, NULL) != SQLITE_OK)
-				return -1;
-			break;
-		case 't':
-			if (sqlite3_bind_int64(stmt->handle, i++, va_arg(ap, time_t)) != SQLITE_OK)
-				return -1;
-			break;
-		case ' ':
-			break;
-		default:
-			return errorf("invalid format: %c", *args);
-		}
-	}
-
-	return 0;
-
-sqlite3_err:
-	return errorf("%s", sqlite3_errmsg(s->handle));
-}
-
-static int
-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);
-
-			util_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 0;
-}
-
-int
-save_open(struct save *db, unsigned int idx, enum save_mode mode)
-{
-	assert(db);
-
-	return save_open_path(db, path(idx), mode);
-}
-
-int
-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, assets_init) < 0)
-		goto sqlite3_err;
-
-	return verify(db);
-
-sqlite3_err:
-	errorf("%s", sqlite3_errmsg(db->handle));
-	sqlite3_close(db->handle);
-
-	memset(db, 0, sizeof (*db));
-
-	return -1;
-}
-
-int
-save_ok(const struct save *db)
-{
-	assert(db);
-
-	return db && db->handle;
-}
-
-int
-save_exec(struct save *db, const char *sql, const char *args, ...)
-{
-	assert(save_ok(db));
-	assert(sql);
-
-	struct save_stmt stmt;
-	enum save_stmt_errno ret;
-	va_list ap;
-
-	va_start(ap, args);
-	ret = prepare(db, &stmt, sql, args, ap);
-	va_end(ap);
-
-	if (ret < 0)
-		return -1;
-
-	ret = save_stmt_next(&stmt, NULL);
-	save_stmt_finish(&stmt);
-
-	return ret == SAVE_STMT_ERROR ? -1 : 0;
-}
-
-void
-save_finish(struct save *db)
-{
-	assert(db);
-
-	if (db->handle)
-		sqlite3_close(db->handle);
-
-	memset(db, 0, sizeof (*db));
-}
-
-int
-save_stmt_init(struct save_stmt *stmt, struct save *db, const char *sql, const char *args, ...)
-{
-	assert(stmt);
-	assert(save_ok(db));
-	assert(args);
-
-	va_list ap;
-	int ret;
-
-	va_start(ap, args);
-	ret = prepare(db, stmt, sql, args, ap);
-	va_end(ap);
-
-	return ret;
-}
-
-enum save_stmt_errno
-save_stmt_next(struct save_stmt *stmt, const char *args, ...)
-{
-	assert(stmt);
-
-	va_list ap;
-	enum save_stmt_errno ret = SAVE_STMT_ERROR;
-
-	switch (sqlite3_step(stmt->handle)) {
-	case SQLITE_ROW:
-		va_start(ap, args);
-
-		if (extract(stmt, args, ap) == 0)
-			ret = SAVE_STMT_ROW;
-
-		va_end(ap);
-		break;
-	case SQLITE_DONE:
-		ret = SAVE_STMT_DONE;
-		break;
-	default:
-		errorf("%s", sqlite3_errmsg(stmt->parent->handle));
-		break;
-	}
-
-	return ret;
-}
-
-void
-save_stmt_finish(struct save_stmt *stmt)
-{
-	assert(stmt);
-
-	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	Sat Oct 15 21:19:25 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,87 +0,0 @@
-/*
- * save.h -- save functions
- *
- * Copyright (c) 2020-2022 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 MLK_RPG_SAVE_H
-#define MLK_RPG_SAVE_H
-
-#include <time.h>
-
-#include <mlk/core/core.h>
-
-struct save {
-	time_t created;
-	time_t updated;
-	void *handle;
-};
-
-enum save_mode {
-	SAVE_MODE_READ,
-	SAVE_MODE_WRITE
-};
-
-struct save_stmt {
-	struct save *parent;
-	void *handle;
-};
-
-enum save_stmt_errno {
-	SAVE_STMT_DONE,
-	SAVE_STMT_ROW,
-	SAVE_STMT_ERROR
-};
-
-CORE_BEGIN_DECLS
-
-int
-save_open(struct save *, unsigned int, enum save_mode);
-
-int
-save_open_path(struct save *, const char *, enum save_mode);
-
-int
-save_ok(const struct save *);
-
-int
-save_exec(struct save *, const char *, const char *, ...);
-
-void
-save_finish(struct save *);
-
-/* Prepared statements. */
-int
-save_stmt_init(struct save_stmt *, struct save *, const char *, const char *, ...);
-
-enum save_stmt_errno
-save_stmt_next(struct save_stmt *, const char *, ...);
-
-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 /* !MLK_RPG_SAVE_H */
--- a/src/libmlk-rpg/rpg/selection.c	Sat Oct 15 21:19:25 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,68 +0,0 @@
-/*
- * selection.c -- kind of selection
- *
- * Copyright (c) 2020-2022 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 <mlk/core/util.h>
-
-#include "battle.h"
-#include "character.h"
-#include "selection.h"
-
-static void
-random(struct selection *slt, struct battle_entity **entities, size_t entitiesz)
-{
-	do {
-		slt->index_character = util_nrand(0, entitiesz);
-	} while (!battle_entity_ok(entities[slt->index_character]));
-}
-
-static void
-first(struct selection *slt, struct battle_entity **entities, size_t entitiesz)
-{
-	for (size_t i = 0; i < entitiesz; ++i) {
-		if (battle_entity_ok(entities[i])) {
-			slt->index_character = i;
-			break;
-		}
-	}
-}
-
-void
-selection_first(struct selection *slt, const struct battle *bt)
-{
-	assert(slt);
-	assert(bt);
-
-	if (slt->index_side == 0)
-		first(slt, bt->enemies, bt->enemiesz);
-	else
-		first(slt, bt->team, bt->teamsz);
-}
-
-void
-selection_random(struct selection *slt, const struct battle *bt)
-{
-	assert(slt);
-	assert(bt);
-
-	if (slt->index_side == 0)
-		random(slt, bt->enemies, bt->enemiesz);
-	else
-		random(slt, bt->team, bt->teamsz);
-}
--- a/src/libmlk-rpg/rpg/selection.h	Sat Oct 15 21:19:25 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,60 +0,0 @@
-/*
- * selection.h -- kind of selection
- *
- * Copyright (c) 2020-2022 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 MLK_RPG_SELECTION_H
-#define MLK_RPG_SELECTION_H
-
-#include <mlk/core/core.h>
-
-struct battle;
-
-enum selection_kind {
-	SELECTION_KIND_SELF,
-	SELECTION_KIND_ONE,
-	SELECTION_KIND_ALL,
-	SELECTION_KIND_BOTH
-};
-
-enum selection_side {
-	/* Which side allowed (can be both). */
-	SELECTION_SIDE_TEAM     = (1 << 0),
-	SELECTION_SIDE_ENEMY    = (1 << 1)
-};
-
-struct selection {
-	enum selection_kind allowed_kinds;
-	enum selection_side allowed_sides;
-
-	/* Character index in battle entity array. */
-	unsigned int index_character;
-
-	/* Side index (0 = enemy, 1 = team). */
-	unsigned int index_side;
-};
-
-CORE_BEGIN_DECLS
-
-void
-selection_first(struct selection *, const struct battle *);
-
-void
-selection_random(struct selection *, const struct battle *);
-
-CORE_END_DECLS
-
-#endif /* !MLK_RPG_SELECTION_H */
--- a/src/libmlk-rpg/rpg/spell.c	Sat Oct 15 21:19:25 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,52 +0,0 @@
-/*
- * spell.c -- magic spells
- *
- * Copyright (c) 2020-2022 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 "spell.h"
-
-void
-spell_select(const struct spell *s, const struct battle *bt, struct selection *slt)
-{
-	assert(s && s->select);
-	assert(bt);
-	assert(slt);
-
-	s->select(bt, slt);
-}
-
-void
-spell_action(const struct spell *s, struct battle *bt, struct character *owner, const struct selection *slt)
-{
-	assert(s && s->action);
-	assert(bt);
-	assert(owner);
-	assert(slt);
-
-	s->action(bt, owner, slt);
-}
-
-void
-spell_use(struct spell *s, struct character *owner, const struct selection *slt)
-{
-	assert(s && s->use);
-	assert(owner);
-	assert(slt);
-
-	s->use(owner, slt);
-}
--- a/src/libmlk-rpg/rpg/spell.h	Sat Oct 15 21:19:25 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,67 +0,0 @@
-/*
- * spell.h -- magic spells
- *
- * Copyright (c) 2020-2022 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 MLK_RPG_SPELL_H
-#define MLK_RPG_SPELL_H
-
-#include <mlk/core/core.h>
-
-#include "selection.h"
-
-struct character;
-struct battle;
-struct selection;
-
-enum spell_type {
-	SPELL_TYPE_NEUTRAL,
-	SPELL_TYPE_FIRE,
-	SPELL_TYPE_WIND,
-	SPELL_TYPE_WATER,
-	SPELL_TYPE_EARTH,
-	SPELL_TYPE_CHAOS,
-	SPELL_TYPE_HOLY,
-	SPELL_TYPE_TIME
-};
-
-struct spell {
-	const char *name;
-	const char *description;
-	unsigned int mp;
-	enum spell_type type;
-	enum selection_kind select_kind;
-	enum selection_side select_side;
-
-	void (*select)(const struct battle *, struct selection *);
-	void (*action)(struct battle *, struct character *, const struct selection *);
-	void (*use)(struct character *, const struct selection *);
-};
-
-CORE_BEGIN_DECLS
-
-void
-spell_select(const struct spell *, const struct battle *, struct selection *);
-
-void
-spell_action(const struct spell *, struct battle *, struct character *, const struct selection *);
-
-void
-spell_use(struct spell *, struct character *, const struct selection *);
-
-CORE_END_DECLS
-
-#endif /* !MLK_RPG_SPELL_H */
--- a/src/libmlk-rpg/rpg/team.c	Sat Oct 15 21:19:25 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,20 +0,0 @@
-/*
- * team.c -- team storage
- *
- * Copyright (c) 2020-2022 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. */
-enum { some_compiler_needs_more_than_nothing = 1 };
--- a/src/libmlk-rpg/rpg/team.h	Sat Oct 15 21:19:25 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,30 +0,0 @@
-/*
- * team.h -- team storage
- *
- * Copyright (c) 2020-2022 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 MLK_RPG_TEAM_H
-#define MLK_RPG_TEAM_H
-
-#define TEAM_MEMBER_MAX (4)
-
-struct character;
-
-struct team {
-	struct character *members[TEAM_MEMBER_MAX];
-};
-
-#endif /* MLK_RPG_TEAM_H */
--- a/src/libmlk-rpg/rpg/tileset-file.c	Sat Oct 15 21:19:25 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,334 +0,0 @@
-/*
- * tileset-file.c -- tileset file loader
- *
- * Copyright (c) 2020-2022 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 <errno.h>
-#include <limits.h>
-#include <stddef.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <mlk/util/util.h>
-
-#include <mlk/core/alloc.h>
-#include <mlk/core/animation.h>
-#include <mlk/core/error.h>
-#include <mlk/core/image.h>
-#include <mlk/core/util.h>
-
-#include "tileset-file.h"
-#include "tileset.h"
-
-#define MAX_F(v) MAX_F_(v)
-#define MAX_F_(v) "%" #v "[^\n|]"
-
-/*
- * This is how memory for animations is allocated in the tileset_file
- * structure.
- *
- * As animations require a texture and a sprite to be present, we need to store
- * them locally in the tileset_file structure.
- *
- * tileset_file->anims[0] array (struct tileset_animation_block):
- *
- * [0]            [1]            [N]
- *  | texture      | texture      | texture
- *  | sprite       | sprite       | sprite
- *  | animation    | animation    | animation
- *
- * tileset_file->anims[1] array (struct tileset_animation):
- *
- * [0]            [1]            [N]
- *  | id           | id           | id
- *  | animation ^  | animation ^  | animation ^
- *
- * The second array is the exposed array through the tileset->anims pointer,
- * animations are referenced from the first array. This is because user may need
- * or replace the tileset by itself and as such we need to keep track of the
- * resource the tileset_file has allocated itself.
- */
-
-struct tileset_animation_block {
-	struct texture texture;
-	struct sprite sprite;
-	struct animation animation;
-};
-
-struct context {
-	struct tileset_file *tf;
-	struct tileset *tileset;
-	FILE *fp;
-
-	char basedir[PATH_MAX];
-
-	/*
-	 * The following properties aren't stored in the tileset because they
-	 * are not needed after loading.
-	 */
-	unsigned int tilewidth;
-	unsigned int tileheight;
-
-	/* Number of rows/columns in the image. */
-	unsigned int nrows;
-	unsigned int ncolumns;
-};
-
-static void
-tileset_animation_block_finish(void *data)
-{
-	struct tileset_animation_block *anim = data;
-
-	texture_finish(&anim->texture);
-}
-
-static int
-tileset_tiledef_cmp(const void *d1, const void *d2)
-{
-	const struct tileset_tiledef *mtd1 = d1;
-	const struct tileset_tiledef *mtd2 = d2;
-
-	if (mtd1->id < mtd2->id)
-		return -1;
-	if (mtd1->id > mtd2->id)
-		return 1;
-
-	return 0;
-}
-
-static int
-tileset_animation_cmp(const void *d1, const void *d2)
-{
-	const struct tileset_animation *mtd1 = d1;
-	const struct tileset_animation *mtd2 = d2;
-
-	if (mtd1->id < mtd2->id)
-		return -1;
-	if (mtd1->id > mtd2->id)
-		return 1;
-
-	return 0;
-}
-
-static int
-parse_tilewidth(struct context *ctx, const char *line)
-{
-	if (sscanf(line, "tilewidth|%u", &ctx->tilewidth) != 1 || ctx->tilewidth == 0)
-		return errorf("tilewidth is null");
-
-	return 0;
-}
-
-static int
-parse_tileheight(struct context *ctx, const char *line)
-{
-	if (sscanf(line, "tileheight|%u", &ctx->tileheight) != 1 || ctx->tileheight == 0)
-		return errorf("tileheight is null");
-
-	return 0;
-}
-
-static int
-parse_tiledefs(struct context *ctx, const char *line)
-{
-	(void)line;
-
-	short x, y;
-	unsigned short id, w, h;
-	struct tileset_tiledef *td;
-
-	alloc_pool_init(&ctx->tf->tiledefs, sizeof (*td), NULL);
-
-	while (fscanf(ctx->fp, "%hu|%hd|%hd|%hu|%hu\n", &id, &x, &y, &w, &h) == 5) {
-		td = alloc_pool_new(&ctx->tf->tiledefs);
-		td->id = id;
-		td->x = x;
-		td->y = y;
-		td->w = w;
-		td->h = h;
-	}
-
-	/* Sort the array and expose it through the tileset->tiledefs pointer. */
-	qsort(ctx->tf->tiledefs.data, ctx->tf->tiledefs.size, ctx->tf->tiledefs.elemsize, tileset_tiledef_cmp);
-	ctx->tileset->tiledefs = ctx->tf->tiledefs.data;
-	ctx->tileset->tiledefsz = ctx->tf->tiledefs.size;
-
-	return 0;
-}
-
-static int
-parse_animations(struct context *ctx, const char *line)
-{
-	(void)line;
-
-	unsigned short id;
-	unsigned int delay;
-	char filename[FILENAME_MAX + 1];
-	struct tileset_animation_block *anim;
-
-	alloc_pool_init(&ctx->tf->anims[0], sizeof (struct tileset_animation_block), tileset_animation_block_finish);
-	alloc_pool_init(&ctx->tf->anims[1], sizeof (struct tileset_animation), NULL);
-
-	/*
-	 * 1. Create the first array of animation, sprite and texture that are
-	 *    owned by the tileset_file structure.
-	 */
-	while (fscanf(ctx->fp, "%hu|" MAX_F(FILENAME_MAX) "|%u", &id, filename, &delay) == 3) {
-		anim = alloc_pool_new(&ctx->tf->anims[0]);
-
-		if (image_open(&anim->texture, util_pathf("%s/%s", ctx->basedir, filename)) < 0)
-			return -1;
-
-		sprite_init(&anim->sprite, &anim->texture, ctx->tilewidth, ctx->tileheight);
-		animation_init(&anim->animation, &anim->sprite, delay);
-	}
-
-	/*
-	 * 2. Create the second array that only consist of pointers to
-	 *    animations referencing the first array.
-	 */
-	for (size_t i = 0; i < ctx->tf->anims[0].size; ++i) {
-		struct tileset_animation_block *anim = alloc_pool_get(&ctx->tf->anims[0], i);
-		struct tileset_animation *ta;
-
-		if (!(ta = alloc_pool_new(&ctx->tf->anims[1])))
-			return -1;
-
-		ta->id = id;
-		ta->animation = &anim->animation;
-	}
-
-	/*
-	 * 3. Finally expose the second array through the tileset->anims pointer
-	 *    and sort it.
-	 */
-	qsort(ctx->tf->anims[1].data, ctx->tf->anims[1].size, ctx->tf->anims[1].elemsize, tileset_animation_cmp);
-	ctx->tileset->anims  = ctx->tf->anims[1].data;
-	ctx->tileset->animsz = ctx->tf->anims[1].size;
-
-	return 0;
-}
-
-static int
-parse_image(struct context *ctx, const char *line)
-{
-	char *p;
-
-	if (ctx->tilewidth == 0 || ctx->tileheight == 0)
-		return errorf("missing tile dimensions before image");
-	if (!(p = strchr(line, '|')))
-		return errorf("could not parse image");
-
-	if (image_open(&ctx->tf->image, util_pathf("%s/%s", ctx->basedir, p + 1)) < 0)
-		return -1;
-
-	sprite_init(&ctx->tf->sprite, &ctx->tf->image, ctx->tilewidth, ctx->tileheight);
-	ctx->tileset->sprite = &ctx->tf->sprite;
-
-	return 0;
-}
-
-static int
-parse_line(struct context *ctx, const char *line)
-{
-	static const struct {
-		const char *property;
-		int (*read)(struct context *, const char *);
-	} props[] = {
-		{ "tilewidth",  parse_tilewidth         },
-		{ "tileheight", parse_tileheight        },
-		{ "tiledefs",   parse_tiledefs          },
-		{ "animations", parse_animations        },
-		{ "image",      parse_image             }
-	};
-
-	for (size_t i = 0; i < UTIL_SIZE(props); ++i) {
-		if (strncmp(line, props[i].property, strlen(props[i].property)) == 0)
-			return props[i].read(ctx, line);
-	}
-
-	return 0;
-}
-
-static int
-parse(struct context *ctx, const char *path)
-{
-	char line[1024];
-	char basedir[PATH_MAX];
-
-	util_strlcpy(basedir, path, sizeof (basedir));
-	util_strlcpy(ctx->basedir, util_dirname(basedir), sizeof (ctx->basedir));
-
-	while (fgets(line, sizeof (line), ctx->fp)) {
-		/* Remove \n if any */
-		line[strcspn(line, "\r\n")] = '\0';
-
-		if (parse_line(ctx, line) < 0)
-			return -1;
-	}
-
-	return 0;
-}
-
-static int
-check(const struct tileset *tileset)
-{
-	if (!tileset->sprite)
-		return errorf("missing tileset image");
-
-	return 0;
-}
-
-int
-tileset_file_open(struct tileset_file *tf, struct tileset *tileset, const char *path)
-{
-	assert(tf);
-	assert(tileset);
-	assert(path);
-
-	struct context ctx = {
-		.tf = tf,
-		.tileset = tileset
-	};
-	int ret = 0;
-
-	memset(tileset, 0, sizeof (*tileset));
-
-	if (!(ctx.fp = fopen(path, "r")))
-		return -1;
-	if ((ret = parse(&ctx, path)) < 0 || (ret = check(tileset)) < 0)
-		tileset_file_finish(tf);
-
-	fclose(ctx.fp);
-
-	return ret;
-}
-
-void
-tileset_file_finish(struct tileset_file *tf)
-{
-	assert(tf);
-
-	alloc_pool_finish(&tf->tiledefs);
-	alloc_pool_finish(&tf->anims[0]);
-	alloc_pool_finish(&tf->anims[1]);
-
-	texture_finish(&tf->image);
-
-	memset(tf, 0, sizeof (*tf));
-}
--- a/src/libmlk-rpg/rpg/tileset-file.h	Sat Oct 15 21:19:25 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,49 +0,0 @@
-/*
- * tileset-file.h -- tileset file loader
- *
- * Copyright (c) 2020-2022 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 MLK_RPG_TILESET_FILE_H
-#define MLK_RPG_TILESET_FILE_H
-
-#include <stddef.h>
-
-#include <mlk/core/alloc.h>
-#include <mlk/core/core.h>
-#include <mlk/core/sprite.h>
-#include <mlk/core/texture.h>
-
-struct tileset;
-struct tileset_tiledef;
-
-struct tileset_file {
-	struct alloc_pool tiledefs;
-	struct alloc_pool anims[2];
-	struct texture image;
-	struct sprite sprite;
-};
-
-CORE_BEGIN_DECLS
-
-int
-tileset_file_open(struct tileset_file *, struct tileset *, const char *);
-
-void
-tileset_file_finish(struct tileset_file *);
-
-CORE_END_DECLS
-
-#endif /* !MLK_RPG_TILESET_FILE_H */
--- a/src/libmlk-rpg/rpg/tileset.c	Sat Oct 15 21:19:25 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,93 +0,0 @@
-/*
- * tileset.c -- map tileset definition
- *
- * Copyright (c) 2020-2022 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 <stdlib.h>
-
-#include <mlk/core/animation.h>
-#include <mlk/core/sprite.h>
-
-#include "tileset.h"
-
-static inline int
-anim_cmp(const void *d1, const void *d2)
-{
-	const struct tileset_animation *mtd1 = d1;
-	const struct tileset_animation *mtd2 = d2;
-
-	if (mtd1->id < mtd2->id)
-		return -1;
-	if (mtd1->id > mtd2->id)
-		return 1;
-
-	return 0;
-}
-
-static inline const struct tileset_animation *
-find(const struct tileset *ts, unsigned int r, unsigned int c)
-{
-	const struct tileset_animation key = {
-		.id = c + (r * ts->sprite->ncols)
-	};
-
-	return bsearch(&key, ts->anims, ts->animsz, sizeof (key), anim_cmp);
-}
-
-int
-tileset_ok(const struct tileset *ts)
-{
-	return ts && sprite_ok(ts->sprite);
-}
-
-void
-tileset_start(struct tileset *ts)
-{
-	for (size_t i = 0; i < ts->animsz; ++i) {
-		struct tileset_animation *ta = &ts->anims[i];
-
-		if (ta->animation)
-			animation_start(ta->animation);
-	}
-}
-
-void
-tileset_update(struct tileset *ts, unsigned int ticks)
-{
-	for (size_t i = 0; i < ts->animsz; ++i) {
-		struct tileset_animation *ta = &ts->anims[i];
-
-		if (!ta->animation)
-			continue;
-
-		if (animation_update(ta->animation, ticks))
-			animation_start(ta->animation);
-	}
-}
-
-void
-tileset_draw(const struct tileset *ts, unsigned int r, unsigned int c, int x, int y)
-{
-	assert(ts);
-
-	const struct tileset_animation *ta;
-
-	if ((ta = find(ts, r, c)))
-		animation_draw(ta->animation, x, y);
-	else
-		sprite_draw(ts->sprite, r, c, x, y);
-}
--- a/src/libmlk-rpg/rpg/tileset.h	Sat Oct 15 21:19:25 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,65 +0,0 @@
-/*
- * tileset.h -- map tileset definition
- *
- * Copyright (c) 2020-2022 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 MLK_RPG_TILESET_H
-#define MLK_RPG_TILESET_H
-
-#include <stddef.h>
-
-#include <mlk/core/core.h>
-
-struct sprite;
-
-struct tileset_tiledef {
-	unsigned short id;
-	short x;
-	short y;
-	unsigned short w;
-	unsigned short h;
-};
-
-struct tileset_animation {
-	unsigned short id;
-	struct animation *animation;
-};
-
-struct tileset {
-	struct tileset_tiledef *tiledefs;
-	size_t tiledefsz;
-	struct tileset_animation *anims;
-	size_t animsz;
-	struct sprite *sprite;
-};
-
-CORE_BEGIN_DECLS
-
-int
-tileset_ok(const struct tileset *);
-
-void
-tileset_start(struct tileset *);
-
-void
-tileset_update(struct tileset *, unsigned int);
-
-void
-tileset_draw(const struct tileset *, unsigned int, unsigned int, int, int);
-
-CORE_END_DECLS
-
-#endif /* !MLK_RPG_TILESET_H */
--- a/src/libmlk-rpg/rpg/walksprite.c	Sat Oct 15 21:19:25 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,69 +0,0 @@
-/*
- * walksprite.c -- sprite designed for walking entities
- *
- * Copyright (c) 2020-2022 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 <string.h>
-
-#include <mlk/core/sprite.h>
-
-#include "walksprite.h"
-
-void
-walksprite_init(struct walksprite *ws, struct sprite *sprite, unsigned int delay)
-{
-	assert(ws);
-	assert(sprite);
-
-	memset(ws, 0, sizeof (*ws));
-	ws->sprite = sprite;
-	ws->delay = delay;
-}
-
-void
-walksprite_reset(struct walksprite *ws)
-{
-	assert(ws);
-
-	ws->index = 0;
-}
-
-void
-walksprite_update(struct walksprite *ws, unsigned int ticks)
-{
-	assert(ws);
-
-	ws->elapsed += ticks;
-
-	if (ws->elapsed >= ws->delay) {
-		ws->index += 1;
-
-		if (ws->index >= ws->sprite->ncols)
-			ws->index = 0;
-
-		ws->elapsed = 0;
-	}
-}
-
-void
-walksprite_draw(const struct walksprite *ws, unsigned int orientation, int x, int y)
-{
-	assert(ws);
-	assert(orientation < 8);
-
-	sprite_draw(ws->sprite, orientation, ws->index, x, y);
-}
--- a/src/libmlk-rpg/rpg/walksprite.h	Sat Oct 15 21:19:25 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,79 +0,0 @@
-/*
- * walksprite.h -- sprite designed for walking entities
- *
- * Copyright (c) 2020-2022 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 MLK_RPG_WALKSPRITE_H
-#define MLK_RPG_WALKSPRITE_H
-
-#include <mlk/core/core.h>
-
-struct sprite;
-
-/**
- * \brief Sprite designed for walking entities.
- *
- * This structure works with sprite images that are defined as using the
- * following conventions:
- *
- * ```
- * 7   0   1
- *   ↖ ↑ ↗
- * 6 ←   → 2
- *   ↙ ↓ ↘
- * 5   4   3
- * ```
- *
- * Where numbers define row in the sprite according to the character
- * orientation. In other terms, your image sprite should look like this:
- *
- * ```
- * row columns in your image
- * ---|---------------------
- *  0 | ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
- *  1 | ↗↗↗↗↗↗↗↗↗↗↗↗↗↗↗↗↗↗↗↗
- *  2 | →→→→→→→→→→→→→→→→→→→→
- *  3 | ↘↘↘↘↘↘↘↘↘↘↘↘↘↘↘↘↘↘↘↘
- *  4 | ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
- *  5 | ↙↙↙↙↙↙↙↙↙↙↙↙↙↙↙↙↙↙↙↙
- *  6 | ←←←←←←←←←←←←←←←←←←←←
- *  7 | ↖↖↖↖↖↖↖↖↖↖↖↖↖↖↖↖↖↖↖↖
- * ```
- */
-struct walksprite {
-	struct sprite *sprite;
-	unsigned int delay;
-	unsigned int index;
-	unsigned int elapsed;
-};
-
-CORE_BEGIN_DECLS
-
-void
-walksprite_init(struct walksprite *, struct sprite *, unsigned int);
-
-void
-walksprite_reset(struct walksprite *);
-
-void
-walksprite_update(struct walksprite *, unsigned int);
-
-void
-walksprite_draw(const struct walksprite *, unsigned int, int, int);
-
-CORE_END_DECLS
-
-#endif /* !MLK_RPG_WALKSPRITE_H */
--- a/tests/test-character.c	Sat Oct 15 21:19:25 2022 +0200
+++ b/tests/test-character.c	Sat Oct 15 21:24:17 2022 +0200
@@ -19,8 +19,8 @@
 #include <stdio.h>
 #include <string.h>
 
-#include <rpg/character.h>
-#include <rpg/save.h>
+#include <mlk/rpg/character.h>
+#include <mlk/rpg/save.h>
 
 #include <dt.h>
 
--- a/tests/test-map.c	Sat Oct 15 21:19:25 2022 +0200
+++ b/tests/test-map.c	Sat Oct 15 21:24:17 2022 +0200
@@ -22,8 +22,8 @@
 #include <mlk/core/sys.h>
 #include <mlk/core/window.h>
 
-#include <rpg/map-file.h>
-#include <rpg/map.h>
+#include <mlk/rpg/map-file.h>
+#include <mlk/rpg/map.h>
 
 #include <dt.h>
 
--- a/tests/test-save-quest.c	Sat Oct 15 21:19:25 2022 +0200
+++ b/tests/test-save-quest.c	Sat Oct 15 21:24:17 2022 +0200
@@ -20,8 +20,8 @@
 
 #include <mlk/core/util.h>
 
-#include <rpg/quest.h>
-#include <rpg/save.h>
+#include <mlk/rpg/quest.h>
+#include <mlk/rpg/save.h>
 
 #include <dt.h>
 
--- a/tests/test-save.c	Sat Oct 15 21:19:25 2022 +0200
+++ b/tests/test-save.c	Sat Oct 15 21:24:17 2022 +0200
@@ -18,8 +18,8 @@
 
 #include <stdio.h>
 
-#include <rpg/property.h>
-#include <rpg/save.h>
+#include <mlk/rpg/property.h>
+#include <mlk/rpg/save.h>
 
 #include <dt.h>
 
--- a/tests/test-tileset.c	Sat Oct 15 21:19:25 2022 +0200
+++ b/tests/test-tileset.c	Sat Oct 15 21:24:17 2022 +0200
@@ -19,8 +19,8 @@
 #include <mlk/core/core.h>
 #include <mlk/core/window.h>
 
-#include <rpg/tileset-file.h>
-#include <rpg/tileset.h>
+#include <mlk/rpg/tileset-file.h>
+#include <mlk/rpg/tileset.h>
 
 #include <dt.h>