changeset 148:c577c15df07f

misc: split libraries, closes #2496
author David Demelier <markand@malikania.fr>
date Thu, 15 Oct 2020 10:32:18 +0200
parents b386d25832c8
children a43e79d489ea
files CMakeLists.txt doxygen/CMakeLists.txt examples/CMakeLists.txt examples/example-action.c examples/example-drawable.c examples/example-font.c examples/example-inventory.c examples/example-label.c examples/example-message.c examples/example-sound.c examples/example-trace.c libadventure/CMakeLists.txt libadventure/adventure/mainmenu_state.c libadventure/adventure/panic_state.c libadventure/adventure/splashscreen_state.c libadventure/adventure/trace_hud.c libcore/CMakeLists.txt libcore/core/button.c libcore/core/button.h libcore/core/checkbox.c libcore/core/checkbox.h libcore/core/debug.c libcore/core/debug.h libcore/core/frame.c libcore/core/frame.h libcore/core/inventory.c libcore/core/inventory.h libcore/core/inventory_dialog.c libcore/core/inventory_dialog.h libcore/core/item.h libcore/core/label.c libcore/core/label.h libcore/core/map.c libcore/core/map.h libcore/core/map_state.c libcore/core/map_state.h libcore/core/message.c libcore/core/message.h libcore/core/theme.c libcore/core/theme.h libcore/core/walksprite.c libcore/core/walksprite.h librpg/CMakeLists.txt librpg/rpg/inventory.c librpg/rpg/inventory.h librpg/rpg/inventory_dialog.c librpg/rpg/inventory_dialog.h librpg/rpg/item.h librpg/rpg/map.c librpg/rpg/map.h librpg/rpg/map_state.c librpg/rpg/map_state.h librpg/rpg/message.c librpg/rpg/message.h librpg/rpg/walksprite.c librpg/rpg/walksprite.h libui/CMakeLists.txt libui/ui/button.c libui/ui/button.h libui/ui/checkbox.c libui/ui/checkbox.h libui/ui/debug.c libui/ui/debug.h libui/ui/frame.c libui/ui/frame.h libui/ui/label.c libui/ui/label.h libui/ui/theme.c libui/ui/theme.h molko/main.c tests/test-inventory.c tests/test-map.c
diffstat 72 files changed, 3582 insertions(+), 3480 deletions(-) [+]
line wrap: on
line diff
--- a/CMakeLists.txt	Thu Oct 15 09:21:04 2020 +0200
+++ b/CMakeLists.txt	Thu Oct 15 10:32:18 2020 +0200
@@ -53,6 +53,8 @@
 add_subdirectory(tools/map)
 
 add_subdirectory(libcore)
+add_subdirectory(libui)
+add_subdirectory(librpg)
 add_subdirectory(libadventure)
 add_subdirectory(molko)
 
--- a/doxygen/CMakeLists.txt	Thu Oct 15 09:21:04 2020 +0200
+++ b/doxygen/CMakeLists.txt	Thu Oct 15 10:32:18 2020 +0200
@@ -30,12 +30,16 @@
 		doxygen/page-examples.c
 		libadventure
 		libcore
+		libui
+		librpg
 	)
 
 	set(
 		DOXYGEN_STRIP_FROM_PATH
 		${CMAKE_SOURCE_DIR}/libadventure
 		${CMAKE_SOURCE_DIR}/libcore
+		${CMAKE_SOURCE_DIR}/libui
+		${CMAKE_SOURCE_DIR}/librpg
 	)
 
 	set(DOXYGEN_ALLOW_UNICODE_NAMES YES)
--- a/examples/CMakeLists.txt	Thu Oct 15 09:21:04 2020 +0200
+++ b/examples/CMakeLists.txt	Thu Oct 15 10:32:18 2020 +0200
@@ -25,37 +25,37 @@
 	ASSETS
 		${examples_SOURCE_DIR}/assets/sprites/chest.png
 		${examples_SOURCE_DIR}/assets/sprites/people.png
-	LIBRARIES libcore
+	LIBRARIES librpg
 )
 
 molko_define_executable(
 	TARGET example-font
 	SOURCES example-font.c
-	LIBRARIES libcore
+	LIBRARIES libui
 )
 
 molko_define_executable(
 	TARGET example-inventory
-	SOURCES example-inventory
+	SOURCES example-inventory.c
 	ASSETS
 		${examples_SOURCE_DIR}/assets/images/fish.png
 		${examples_SOURCE_DIR}/assets/images/potion.png
 		${examples_SOURCE_DIR}/assets/images/sword.png
 	FOLDER examples
-	LIBRARIES libcore
+	LIBRARIES librpg
 )
 
 molko_define_executable(
 	TARGET example-label
 	SOURCES example-label.c
-	LIBRARIES libcore
+	LIBRARIES libui
 )
 
 molko_define_executable(
 	TARGET example-message
 	SOURCES example-message.c
 	FOLDER examples
-	LIBRARIES libcore
+	LIBRARIES librpg
 )
 
 molko_define_executable(
@@ -63,15 +63,15 @@
 	SOURCES example-sound.c
 	FOLDER examples
 	ASSETS
-		${examples_SOURCE_DIR}/assets/sounds/vabsounds-romance.ogg 
-	LIBRARIES libcore
+		${examples_SOURCE_DIR}/assets/sounds/vabsounds-romance.ogg
+	LIBRARIES libui
 )
 
 molko_define_executable(
 	TARGET example-drawable
 	SOURCES example-drawable.c
 	FOLDER examples
-	LIBRARIES libcore
+	LIBRARIES libui
 	ASSETS
 		${examples_SOURCE_DIR}/assets/sprites/explosion.png
 )
@@ -80,5 +80,5 @@
 	TARGET example-trace
 	SOURCES example-trace.c
 	FOLDER examples
-	LIBRARIES libcore libadventure
+	LIBRARIES libui libadventure
 )
--- a/examples/example-action.c	Thu Oct 15 09:21:04 2020 +0200
+++ b/examples/example-action.c	Thu Oct 15 10:32:18 2020 +0200
@@ -23,17 +23,19 @@
 #include <core/event.h>
 #include <core/image.h>
 #include <core/maths.h>
-#include <core/message.h>
 #include <core/painter.h>
 #include <core/panic.h>
 #include <core/script.h>
 #include <core/sprite.h>
 #include <core/sys.h>
 #include <core/texture.h>
-#include <core/theme.h>
 #include <core/util.h>
 #include <core/window.h>
 
+#include <ui/theme.h>
+
+#include <rpg/message.h>
+
 #include <assets/sprites/chest.h>
 #include <assets/sprites/people.h>
 
--- a/examples/example-drawable.c	Thu Oct 15 09:21:04 2020 +0200
+++ b/examples/example-drawable.c	Thu Oct 15 10:32:18 2020 +0200
@@ -23,17 +23,18 @@
 #include <core/event.h>
 #include <core/drawable.h>
 #include <core/key.h>
-#include <core/label.h>
 #include <core/painter.h>
 #include <core/panic.h>
 #include <core/sys.h>
 #include <core/image.h>
 #include <core/sprite.h>
 #include <core/texture.h>
-#include <core/theme.h>
 #include <core/util.h>
 #include <core/window.h>
 
+#include <ui/label.h>
+#include <ui/theme.h>
+
 #include <assets/sprites/explosion.h>
 
 #define W 1280
--- a/examples/example-font.c	Thu Oct 15 09:21:04 2020 +0200
+++ b/examples/example-font.c	Thu Oct 15 10:32:18 2020 +0200
@@ -23,10 +23,11 @@
 #include <core/panic.h>
 #include <core/sys.h>
 #include <core/texture.h>
-#include <core/theme.h>
 #include <core/util.h>
 #include <core/window.h>
 
+#include <ui/theme.h>
+
 #define W       (1280)
 #define H       (720)
 
--- a/examples/example-inventory.c	Thu Oct 15 09:21:04 2020 +0200
+++ b/examples/example-inventory.c	Thu Oct 15 10:32:18 2020 +0200
@@ -19,17 +19,19 @@
 #include <core/clock.h>
 #include <core/event.h>
 #include <core/image.h>
-#include <core/inventory.h>
-#include <core/inventory_dialog.h>
-#include <core/item.h>
 #include <core/painter.h>
 #include <core/panic.h>
 #include <core/sys.h>
 #include <core/texture.h>
-#include <core/theme.h>
 #include <core/util.h>
 #include <core/window.h>
 
+#include <ui/theme.h>
+
+#include <rpg/item.h>
+#include <rpg/inventory.h>
+#include <rpg/inventory_dialog.h>
+
 /* https://shikashiassets.itch.io/shikashis-fantasy-icons-pack */
 #include <assets/images/fish.h>
 #include <assets/images/potion.h>
--- a/examples/example-label.c	Thu Oct 15 09:21:04 2020 +0200
+++ b/examples/example-label.c	Thu Oct 15 10:32:18 2020 +0200
@@ -18,14 +18,15 @@
 
 #include <core/clock.h>
 #include <core/event.h>
-#include <core/label.h>
 #include <core/painter.h>
 #include <core/panic.h>
 #include <core/sys.h>
-#include <core/theme.h>
 #include <core/util.h>
 #include <core/window.h>
 
+#include <ui/label.h>
+#include <ui/theme.h>
+
 #define W       (1280)
 #define H       (720)
 
--- a/examples/example-message.c	Thu Oct 15 09:21:04 2020 +0200
+++ b/examples/example-message.c	Thu Oct 15 10:32:18 2020 +0200
@@ -20,15 +20,17 @@
 
 #include <core/clock.h>
 #include <core/event.h>
-#include <core/frame.h>
-#include <core/message.h>
 #include <core/painter.h>
 #include <core/panic.h>
 #include <core/sys.h>
 #include <core/util.h>
-#include <core/theme.h>
 #include <core/window.h>
 
+#include <ui/frame.h>
+#include <ui/theme.h>
+
+#include <rpg/message.h>
+
 #define W       (1280)
 #define H       (720)
 
--- a/examples/example-sound.c	Thu Oct 15 09:21:04 2020 +0200
+++ b/examples/example-sound.c	Thu Oct 15 10:32:18 2020 +0200
@@ -18,15 +18,16 @@
 
 #include <core/clock.h>
 #include <core/event.h>
-#include <core/label.h>
 #include <core/painter.h>
 #include <core/panic.h>
 #include <core/sound.h>
 #include <core/sys.h>
-#include <core/theme.h>
 #include <core/util.h>
 #include <core/window.h>
 
+#include <ui/label.h>
+#include <ui/theme.h>
+
 /* https://freesound.org/people/VABsounds/sounds/423658 */
 #include <assets/sounds/vabsounds-romance.h>
 
--- a/examples/example-trace.c	Thu Oct 15 09:21:04 2020 +0200
+++ b/examples/example-trace.c	Thu Oct 15 10:32:18 2020 +0200
@@ -20,12 +20,13 @@
 #include <core/event.h>
 #include <core/sys.h>
 #include <core/window.h>
-#include <core/theme.h>
 #include <core/painter.h>
 #include <core/panic.h>
 #include <core/trace.h>
 #include <core/util.h>
 
+#include <ui/theme.h>
+
 #include <adventure/trace_hud.h>
 
 #define W 1280
--- a/libadventure/CMakeLists.txt	Thu Oct 15 09:21:04 2020 +0200
+++ b/libadventure/CMakeLists.txt	Thu Oct 15 10:32:18 2020 +0200
@@ -44,7 +44,7 @@
 	TARGET libadventure
 	SOURCES ${SOURCES}
 	ASSETS ${ASSETS}
-	LIBRARIES libcore
+	LIBRARIES libcore libui librpg
 	PUBLIC_INCLUDES
 		$<BUILD_INTERFACE:${libadventure_SOURCE_DIR}>
 )
--- a/libadventure/adventure/mainmenu_state.c	Thu Oct 15 09:21:04 2020 +0200
+++ b/libadventure/adventure/mainmenu_state.c	Thu Oct 15 10:32:18 2020 +0200
@@ -24,7 +24,6 @@
 #include <core/font.h>
 #include <core/game.h>
 #include <core/image.h>
-#include <core/map_state.h>
 #include <core/painter.h>
 #include <core/panic.h>
 #include <core/state.h>
@@ -37,6 +36,8 @@
 #include <adventure/assets/tilesets/world.h>
 #include <adventure/assets/sprites/john.h>
 
+#include <rpg/map_state.h>
+
 #include "mainmenu_state.h"
 #include "splashscreen_state.h"
 
--- a/libadventure/adventure/panic_state.c	Thu Oct 15 09:21:04 2020 +0200
+++ b/libadventure/adventure/panic_state.c	Thu Oct 15 10:32:18 2020 +0200
@@ -29,7 +29,8 @@
 #include <core/texture.h>
 #include <core/util.h>
 #include <core/window.h>
-#include <core/map_state.h>
+
+#include <rpg/map_state.h>
 
 #include <adventure/assets/fonts/lato.h>
 
--- a/libadventure/adventure/splashscreen_state.c	Thu Oct 15 09:21:04 2020 +0200
+++ b/libadventure/adventure/splashscreen_state.c	Thu Oct 15 10:32:18 2020 +0200
@@ -19,8 +19,6 @@
 #include <core/font.h>
 #include <core/game.h>
 #include <core/image.h>
-#include <core/map.h>
-#include <core/map_state.h>
 #include <core/painter.h>
 #include <core/panic.h>
 #include <core/state.h>
@@ -28,6 +26,9 @@
 #include <core/texture.h>
 #include <core/window.h>
 
+#include <rpg/map.h>
+#include <rpg/map_state.h>
+
 #include <adventure/assets/fonts/teutonic.h>
 
 #include "splashscreen_state.h"
--- a/libadventure/adventure/trace_hud.c	Thu Oct 15 09:21:04 2020 +0200
+++ b/libadventure/adventure/trace_hud.c	Thu Oct 15 10:32:18 2020 +0200
@@ -20,10 +20,12 @@
 #include <stdio.h>
 #include <string.h>
 
-#include <core/label.h>
 #include <core/font.h>
-#include <core/theme.h>
 #include <core/trace.h>
+#include <core/window.h>
+
+#include <ui/label.h>
+#include <ui/theme.h>
 
 #include "trace_hud.h"
 
@@ -98,7 +100,7 @@
 			.y = y,
 			.text = data.lines[i],
 			.theme = th,
-			.flags = LABEL_ALIGN_LEFT
+			.align = LABEL_ALIGN_TOP_LEFT
 		});
 
 		y += font_height(th->fonts[THEME_FONT_INTERFACE]);
--- a/libcore/CMakeLists.txt	Thu Oct 15 09:21:04 2020 +0200
+++ b/libcore/CMakeLists.txt	Thu Oct 15 10:32:18 2020 +0200
@@ -31,15 +31,9 @@
 	${libcore_SOURCE_DIR}/core/action.h
 	${libcore_SOURCE_DIR}/core/animation.c
 	${libcore_SOURCE_DIR}/core/animation.h
-	${libcore_SOURCE_DIR}/core/button.c
-	${libcore_SOURCE_DIR}/core/button.h
-	${libcore_SOURCE_DIR}/core/checkbox.c
-	${libcore_SOURCE_DIR}/core/checkbox.h
 	${libcore_SOURCE_DIR}/core/clock.c
 	${libcore_SOURCE_DIR}/core/clock.h
 	${libcore_SOURCE_DIR}/core/color.h
-	${libcore_SOURCE_DIR}/core/debug.c
-	${libcore_SOURCE_DIR}/core/debug.h
 	${libcore_SOURCE_DIR}/core/drawable.c
 	${libcore_SOURCE_DIR}/core/drawable.h
 	${libcore_SOURCE_DIR}/core/error.c
@@ -49,30 +43,15 @@
 	${libcore_SOURCE_DIR}/core/event.h
 	${libcore_SOURCE_DIR}/core/font.c
 	${libcore_SOURCE_DIR}/core/font.h
-	${libcore_SOURCE_DIR}/core/frame.c
-	${libcore_SOURCE_DIR}/core/frame.h
 	${libcore_SOURCE_DIR}/core/game.c
 	${libcore_SOURCE_DIR}/core/game.h
 	${libcore_SOURCE_DIR}/core/image.c
 	${libcore_SOURCE_DIR}/core/image.h
 	${libcore_SOURCE_DIR}/core/inhibit.c
 	${libcore_SOURCE_DIR}/core/inhibit.h
-	${libcore_SOURCE_DIR}/core/inventory.c
-	${libcore_SOURCE_DIR}/core/inventory.h
-	${libcore_SOURCE_DIR}/core/inventory_dialog.c
-	${libcore_SOURCE_DIR}/core/inventory_dialog.h
-	${libcore_SOURCE_DIR}/core/item.h
 	${libcore_SOURCE_DIR}/core/key.h
-	${libcore_SOURCE_DIR}/core/label.c
-	${libcore_SOURCE_DIR}/core/label.h
-	${libcore_SOURCE_DIR}/core/map.c
-	${libcore_SOURCE_DIR}/core/map.h
-	${libcore_SOURCE_DIR}/core/map_state.c
-	${libcore_SOURCE_DIR}/core/map_state.h
 	${libcore_SOURCE_DIR}/core/maths.c
 	${libcore_SOURCE_DIR}/core/maths.h
-	${libcore_SOURCE_DIR}/core/message.c
-	${libcore_SOURCE_DIR}/core/message.h
 	${libcore_SOURCE_DIR}/core/mouse.h
 	${libcore_SOURCE_DIR}/core/painter.c
 	${libcore_SOURCE_DIR}/core/painter.h
@@ -95,16 +74,12 @@
 	${libcore_SOURCE_DIR}/core/texture.c
 	${libcore_SOURCE_DIR}/core/texture.h
 	${libcore_SOURCE_DIR}/core/texture_p.h
-	${libcore_SOURCE_DIR}/core/theme.c
-	${libcore_SOURCE_DIR}/core/theme.h
 	${libcore_SOURCE_DIR}/core/trace.c
 	${libcore_SOURCE_DIR}/core/trace.h
 	${libcore_SOURCE_DIR}/core/util.c
 	${libcore_SOURCE_DIR}/core/util.h
 	${libcore_SOURCE_DIR}/core/wait.c
 	${libcore_SOURCE_DIR}/core/wait.h
-	${libcore_SOURCE_DIR}/core/walksprite.c
-	${libcore_SOURCE_DIR}/core/walksprite.h
 	${libcore_SOURCE_DIR}/core/window.c
 	${libcore_SOURCE_DIR}/core/window.h
 	${libcore_SOURCE_DIR}/core/window_p.h
--- a/libcore/core/button.c	Thu Oct 15 09:21:04 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,78 +0,0 @@
-/*
- * button.c -- GUI button
- *
- * Copyright (c) 2020 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <assert.h>
-
-#include "button.h"
-#include "event.h"
-#include "maths.h"
-#include "theme.h"
-
-static bool
-is_boxed(const struct button *button, const struct event_click *click)
-{
-	assert(button);
-	assert(click);
-	assert(click->type == EVENT_CLICKDOWN || click->type == EVENT_CLICKUP);
-
-	return maths_is_boxed(button->x, button->y, button->w, button->h,
-	    click->x, click->y);
-}
-
-void
-button_handle(struct button *button, const union event *ev)
-{
-	assert(button);
-	assert(ev);
-
-	switch (ev->type) {
-	case EVENT_CLICKDOWN:
-		if (is_boxed(button, &ev->click))
-			button->state = BUTTON_STATE_PRESSED;
-		break;
-	case EVENT_CLICKUP:
-		/*
-		 * If the button was pressed, indicate that the button is
-		 * finally activated. This let the user to move the cursor
-		 * outside the button to "forget" the press.
-		 */
-		if (!is_boxed(button, &ev->click))
-			button->state = BUTTON_STATE_NONE;
-		else if (button->state == BUTTON_STATE_PRESSED)
-			button->state = BUTTON_STATE_ACTIVATED;
-		break;
-	default:
-		break;
-	}
-}
-
-void
-button_reset(struct button *button)
-{
-	assert(button);
-
-	button->state = BUTTON_STATE_NONE;
-}
-
-void
-button_draw(struct button *button)
-{
-	assert(button);
-
-	theme_draw_button(button->theme, button);
-}
--- a/libcore/core/button.h	Thu Oct 15 09:21:04 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,85 +0,0 @@
-/*
- * button.h -- GUI button
- *
- * Copyright (c) 2020 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef MOLKO_BUTTON_H
-#define MOLKO_BUTTON_H
-
-/**
- * \file button.h
- * \brief GUI button.
- */
-
-union event;
-
-struct theme;
-
-/**
- * \brief Button state.
- */
-enum button_state {
-	BUTTON_STATE_NONE,              /*!< Button is inactive. */
-	BUTTON_STATE_PRESSED,           /*!< Button is currently pressed. */
-	BUTTON_STATE_ACTIVATED          /*!< Button is considered activated. */
-};
-
-/**
- * \brief GUI button.
- */
-struct button {
-	int x;                          /*!< (+) Position in x. */
-	int y;                          /*!< (+) Position in y. */
-	unsigned int w;                 /*!< (+) Width. */
-	unsigned int h;                 /*!< (+) Height. */
-	const char *text;               /*!< (+&) Text to draw. */
-	enum button_state state;        /*!< (+) Button state. */
-	struct theme *theme;            /*!< (+&?) Theme to use. */
-};
-
-/**
- * Handle the event.
- *
- * You should always call this function even if the event is completely
- * unrelated.
- *
- * \pre button != NULL
- * \pre ev != NULL
- * \param button the button
- * \param ev the event
- */
-void
-button_handle(struct button *button, const union event *ev);
-
-/**
- * Use this function once the button has been considered activated.
- *
- * \pre button != NULL
- * \param button the button
- */
-void
-button_reset(struct button *button);
-
-/**
- * Draw the button.
- *
- * \pre button != NULL
- * \param button the button
- */
-void
-button_draw(struct button *button);
-
-#endif /* !MOLKO_BUTTON_H */
--- a/libcore/core/checkbox.c	Thu Oct 15 09:21:04 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,55 +0,0 @@
-/*
- * checkbox.c -- GUI checkbox
- *
- * Copyright (c) 2020 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <assert.h>
-
-#include "checkbox.h"
-#include "theme.h"
-#include "event.h"
-#include "maths.h"
-
-static bool
-is_boxed(const struct checkbox *cb, const struct event_click *click)
-{
-	assert(cb);
-	assert(click && click->type == EVENT_CLICKDOWN);
-
-	return maths_is_boxed(cb->x, cb->y, cb->w, cb->h, click->x, click->y);
-}
-
-void
-checkbox_handle(struct checkbox *cb, const union event *ev)
-{
-	assert(cb);
-	assert(ev);
-
-	switch (ev->type) {
-	case EVENT_CLICKDOWN:
-		if (is_boxed(cb, &ev->click))
-			cb->checked = !cb->checked;
-		break;
-	default:
-		break;
-	}
-}
-
-void
-checkbox_draw(const struct checkbox *cb)
-{
-	theme_draw_checkbox(cb->theme, cb);
-}
--- a/libcore/core/checkbox.h	Thu Oct 15 09:21:04 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,64 +0,0 @@
-/*
- * checkbox.h -- GUI checkbox
- *
- * Copyright (c) 2020 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef MOLKO_CHECKBOX_H
-#define MOLKO_CHECKBOX_H
-
-/**
- * \file checkbox.h
- * \brief GUI checkbox.
- */
-
-#include <stdbool.h>
-
-union event;
-
-/**
- * \brief GUI checkbox.
- */
-struct checkbox {
-	int x;                  /*!< (+) Position in x. */
-	int y;                  /*!< (+) Position in y. */
-	unsigned int w;         /*!< (+) Width. */
-	unsigned int h;         /*!< (+) Height. */
-	const char *label;      /*!< (+&) Text to show. */
-	bool checked;           /*!< (+) Is activated? */
-	struct theme *theme;    /*!< (+&?) Theme to use. */
-};
-
-/**
- * Draw the checkbox.
- *
- * \pre cb != NULL
- * \pre ev != NULL
- * \param cb the checkbox
- * \param ev the event
- */
-void
-checkbox_handle(struct checkbox *cb, const union event *ev);
-
-/**
- * Draw the checkbox.
- *
- * \pre cb != NULL
- * \param cb the checkbox
- */
-void
-checkbox_draw(const struct checkbox *cb);
-
-#endif /* !MOLKO_CHECKBOX_H */
--- a/libcore/core/debug.c	Thu Oct 15 09:21:04 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,82 +0,0 @@
-/*
- * debug.c -- debugging interfaces
- *
- * Copyright (c) 2020 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <assert.h>
-#include <stdio.h>
-
-#include "debug.h"
-#include "font.h"
-#include "texture.h"
-#include "theme.h"
-
-#define PADDING_X 5
-#define PADDING_Y 5
-
-struct debug_options debug_options = {
-#if !defined(NDEBUG)
-	.enable = true
-#endif
-};
-
-void
-debug_printf(struct debug_report *report, const char *fmt, ...)
-{
-	assert(report);
-	assert(fmt);
-
-	if (!debug_options.enable)
-		return;
-
-	va_list ap;
-
-	va_start(ap, fmt);
-	debug_vprintf(report, fmt, ap);
-	va_end(ap);
-}
-
-void
-debug_vprintf(struct debug_report *report, const char *fmt, va_list ap)
-{
-	assert(report);
-	assert(fmt);
-
-	if (!debug_options.enable)
-		return;
-
-	char line[DEBUG_LINE_MAX];
-	struct theme *theme;
-	struct font *font;
-	struct texture tex;
-	unsigned int gapy;
-
-	vsnprintf(line, sizeof (line), fmt, ap);
-
-	theme = report->theme ? report->theme : theme_default();
-	font = theme->fonts[THEME_FONT_DEBUG];
-	font->color = report->color;
-
-	if (!font_render(font, &tex, line))
-		return;
-
-	gapy = (PADDING_Y * (report->count)) +
-	    (font_height(font) * (report->count));
-	report->count++;
-
-	texture_draw(&tex, PADDING_X, gapy);
-	texture_finish(&tex);
-}
--- a/libcore/core/debug.h	Thu Oct 15 09:21:04 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,131 +0,0 @@
-/*
- * debug.h -- debugging interfaces
- *
- * Copyright (c) 2020 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef MOLKO_DEBUG_H
-#define MOLKO_DEBUG_H
-
-/**
- * \file debug.h
- * \brief Debugging interfaces.
- *
- * This module provides functions to draw debugging information within the game
- * window. It is mostly used for information only when state of the game is
- * better inspected via direct window information rather than writing in the
- * console.
- *
- * Predefined core states may print debugging information if
- * debug_options.enabled variable is set to true. However, you need to specify
- * the font to use in order to work.
- *
- * Each call to \ref debug_printf or \ref debug_vprintf automatically adjust
- * next coordinate for rendering the text. As such, user may simply print
- * several lines of text without having to deal with that manually.
- *
- * Example, activate global debugging.
- *
- * \code
- * struct font *f = font_openf("foo.ttf", 10);
- *
- * if (!f)
- * 	error_fatal();
- *
- * debug_options.default_font = f;
- * debug_options.enabled = true;
- * \endcode
- *
- * Example, print debugging information manually.
- *
- * \code
- * struct debug_report report = {
- * 	.color = 0x00ff00ff,
- * 	.font = myfont          // Optional if debug_options.default_font is set.
- * };
- *
- * debug_printf("current position: %d, %d\n", x, y);
- * \endcode
- */
-
-#include <stdbool.h>
-#include <stdarg.h>
-
-#include "font.h"
-
-/**
- * Maximum content length per report.
- */
-#define DEBUG_LINE_MAX 1024
-
-struct theme;
-
-/**
- * \brief Debugging options.
- *
- * Fill this structure with appropriate values to change debugging behavior
- * in core API.
- */
-struct debug_options {
-	bool enable;                    /*!< (+) Enable core API debugging. */
-};
-
-/**
- * \brief Debug context.
- *
- * Use this structure each time you need to print one or more messages.
- */
-struct debug_report {
-	struct theme *theme;            /*!< (+&?) Theme to use. */
-	unsigned long color;            /*!< (+) Font foreground color to use. */
-	unsigned int count;             /*!< (-) Number of messages already printed. */
-};
-
-/**
- * Global debugging options.
- */
-extern struct debug_options debug_options;
-
-/**
- * Convenient macro to initialize \ref debug_report with default values.
- */
-#define DEBUG_INIT_DEFAULTS {                   \
-        .font = debug_options.default_font,     \
-        .color = debug_options.default_color,   \
-        .style = debug_options.default_style    \
-}
-
-/**
- * Print debugging information into the screen.
- *
- * \pre report != NULL
- * \pre fmt != NULL
- * \param report the reporting context
- * \param fmt the printf(3) format string
- * \note The message length must not exceed DEBUG_LINE_MAX, otherwise its
- *       result is truncated.
- */
-void
-debug_printf(struct debug_report *report, const char *fmt, ...);
-
-/**
- * Similar to \ref debug_printf but with a va_list object.
- *
- * \see \ref debug_printf
- */
-void
-debug_vprintf(struct debug_report *report, const char *fmt, va_list ap);
-
-#endif /* !MOLKO_DEBUG_H */
--- a/libcore/core/frame.c	Thu Oct 15 09:21:04 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,30 +0,0 @@
-/*
- * frame.h -- GUI frame
- *
- * Copyright (c) 2020 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <assert.h>
-
-#include "frame.h"
-#include "theme.h"
-
-void
-frame_draw(const struct frame *frame)
-{
-	assert(frame);
-
-	theme_draw_frame(frame->theme, frame);
-}
--- a/libcore/core/frame.h	Thu Oct 15 09:21:04 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,58 +0,0 @@
-/*
- * frame.h -- GUI frame
- *
- * Copyright (c) 2020 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef MOLKO_FRAME_H
-#define MOLKO_FRAME_H
-
-/**
- * \file frame.h
- * \brief GUI frame.
- */
-
-struct theme;
-
-/**
- * \brief Frame style.
- */
-enum frame_style {
-	FRAME_STYLE_NORMAL,
-	FRAME_STYLE_BOX
-};
-
-/**
- * \brief GUI frame.
- */
-struct frame {
-	int x;                  /*!< (+) Position in x. */
-	int y;                  /*!< (+) Position in y. */
-	unsigned int w;         /*!< (+) Width. */
-	unsigned int h;         /*!< (+) Height. */
-	enum frame_style style; /*!< (+) Frame style. */
-	struct theme *theme;    /*!< (+&?) Theme to use. */
-};
-
-/**
- * Draw the frame.
- *
- * \pre frame != NULL
- * \param frame the frame to draw
- */
-void
-frame_draw(const struct frame *frame);
-
-#endif /* !MOLKO_FRAME_H */
--- a/libcore/core/inventory.c	Thu Oct 15 09:21:04 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,210 +0,0 @@
-/*
- * inventory.c -- inventory of items
- *
- * Copyright (c) 2020 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <assert.h>
-#include <stddef.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "inventory.h"
-#include "item.h"
-
-#define INVENTORY_TOTAL (INVENTORY_ROWS_MAX * INVENTORY_COLS_MAX)
-
-static bool
-can_be_used(struct inventory_slot *slot, const struct item *item)
-{
-	assert(item);
-
-	/* Empty slot. */
-	if (!slot->item)
-		return false;
-
-	/* Not same object. */
-	if (strcmp(slot->item->name, item->name) != 0)
-		return false;
-
-	/* No space in this slot. */
-	if (slot->amount >= slot->item->stackable)
-		return false;
-
-	return true;
-}
-
-static struct inventory_slot *
-find(struct inventory *iv, const struct item *item)
-{
-	assert(iv);
-	assert(item);
-
-	/* First pass: find an entry with the same item. */
-	for (unsigned int r = 0; r < INVENTORY_ROWS_MAX; ++r)
-		for (unsigned int c = 0; c < INVENTORY_COLS_MAX; ++c)
-			if (can_be_used(&iv->items[r][c], item))
-				return &iv->items[r][c];
-
-	/* Second pass: try to find an empty slot. */
-	for (unsigned int r = 0; r < INVENTORY_ROWS_MAX; ++r)
-		for (unsigned int c = 0; c < INVENTORY_COLS_MAX; ++c)
-			if (!iv->items[r][c].item)
-				return &iv->items[r][c];
-
-	return NULL;
-}
-
-static unsigned
-provide(struct inventory_slot *slot, struct item *item, unsigned int amount)
-{
-	assert(slot);
-
-	unsigned int avail;
-
-	/* The slot may be empty, make sure it contains this item. */
-	slot->item = item;
-
-	/*
-	 * Example:
-	 *
-	 * The slot has already 10 items.
-	 * The slot item is stackble up to 64 items.
-	 *
-	 * When pushing:
-	 *
-	 * 80: 54 pushed, 26 left
-	 * 30: 30 pushed, 0 left.
-	 */
-	avail = slot->item->stackable - slot->amount;
-
-	if (amount > avail) {
-		slot->amount += avail;
-		amount -= avail;
-	} else {
-		slot->amount += amount;
-		amount = 0;
-	}
-
-	return amount;
-}
-
-static bool
-merge(struct inventory_slot *slot, struct inventory_slot *other)
-{
-	assert(slot);
-	assert(slot->item);
-	assert(other);
-
-	/* Not compatible, return false to let the sorting continue. */
-	if (slot->item != other->item)
-		return false;
-
-	while (slot->amount < slot->item->stackable && other->amount) {
-		slot->amount++;
-		other->amount--;
-	}
-
-	/* No more amount in the other slot, empty it. */
-	if (other->amount == 0U)
-		memset(other, 0, sizeof (*other));
-
-	return slot->amount >= slot->item->stackable;
-}
-
-static void
-sort(struct inventory *iv, struct inventory_slot *slot, int r, int c)
-{
-	assert(slot);
-	assert(slot->item);
-
-	/* Merge until the end of thiw row. */
-	for (c = c + 1; c < INVENTORY_COLS_MAX; ++c)
-		if (merge(slot, &iv->items[r][c]))
-			return;
-
-	/* Merge the next rows. */
-	for (r = r + 1; r < INVENTORY_ROWS_MAX; ++r)
-		for (c = 0; c < INVENTORY_COLS_MAX; ++c)
-			if (merge(slot, &iv->items[r][c]))
-				return;
-}
-
-unsigned int
-inventory_push(struct inventory *iv, struct item *item, unsigned int amount)
-{
-	assert(iv);
-	assert(item);
-
-	while (amount) {
-		struct inventory_slot *slot;
-
-		if (!(slot = find(iv, item)))
-			break;
-
-		/* Add as much as we can in this slot. */
-		amount = provide(slot, item, amount);
-	}
-
-	return amount;
-}
-
-static int
-compare_slot(const void *v1, const void *v2)
-{
-	const struct inventory_slot *slot1 = v1;
-	const struct inventory_slot *slot2 = v2;
-	int cmp;
-
-	/* Two null slots compare equal. */
-	if (!slot1->item && !slot2->item)
-		return 0;
-
-	/* Null left should be moved after. */
-	if (!slot1->item)
-		return 1;
-
-	/* Null right slots should be moved after. */
-	if (!slot2->item)
-		return -1;
-
-	/* If they are identical, use amount to sort. */
-	if ((cmp = strcmp(slot1->item->name, slot2->item->name)) == 0)
-		return (long long int)slot2->amount - (long long int)slot1->amount;
-
-	return cmp;
-}
-
-void
-inventory_sort(struct inventory *iv)
-{
-	assert(iv);
-
-	for (int r = 0; r < INVENTORY_ROWS_MAX; ++r)
-		for (int c = 0; c < INVENTORY_COLS_MAX; ++c)
-			if (iv->items[r][c].item)
-				sort(iv, &iv->items[r][c], r, c);
-
-	/* Sort by names AND by amount. */
-	qsort(iv->items, INVENTORY_TOTAL, sizeof (struct inventory_slot), compare_slot);
-}
-
-void
-inventory_clear(struct inventory *iv)
-{
-	assert(iv);
-
-	memset(iv, 0, sizeof (*iv));
-}
--- a/libcore/core/inventory.h	Thu Oct 15 09:21:04 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,92 +0,0 @@
-/*
- * inventory.h -- inventory of items
- *
- * Copyright (c) 2020 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef MOLKO_INVENTORY_H
-#define MOLKO_INVENTORY_H
-
-/**
- * \file inventory.h
- * \brief Inventory of items.
- */
-
-/**
- * \brief Maximum number of rows.
- */
-#define INVENTORY_ROWS_MAX      3
-
-/**
- * \brief Maximum number of columns.
- */
-#define INVENTORY_COLS_MAX      10
-
-struct item;
-
-/**
- * \brief Inventory slot.
- *
- * This structure describe a 'cell' into the inventory. It references a item
- * and has a given amount of it.
- */
-struct inventory_slot {
-	struct item *item;      /*!< (+&?) Pointer to the item. */
-	unsigned int amount;    /*!< (-) Number of items in this slot. */
-};
-
-/**
- * \brief Inventory structure.
- */
-struct inventory {
-	/**
-	 * (-) Grid of objects.
-	 */
-	struct inventory_slot items[INVENTORY_ROWS_MAX][INVENTORY_COLS_MAX];
-};
-
-/**
- * Try to push as much as possible the given item.
- *
- * \pre iv != NULL
- * \pre item != NULL
- * \param iv the inventory
- * \param item the item to reference
- * \param amount the desired amount
- * \return 0 if all items were pushed or the number left otherwise
- */
-unsigned int
-inventory_push(struct inventory *iv, struct item *item, unsigned int amount);
-
-/**
- * Sort the inventory.
- *
- * \pre iv != NULL
- * \pre item != NULL
- * \param iv the inventory
- */
-void
-inventory_sort(struct inventory *iv);
-
-/**
- * Clears the inventory.
- *
- * \pre iv != NULL
- * \param iv the inventory
- */
-void
-inventory_clear(struct inventory *iv);
-
-#endif /* !MOLKO_INVENTORY_H */
--- a/libcore/core/inventory_dialog.c	Thu Oct 15 09:21:04 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,324 +0,0 @@
-/*
- * inventory_dialog.h -- dialog for items
- *
- * Copyright (c) 2020 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <assert.h>
-#include <stdio.h>
-
-#include "button.h"
-#include "event.h"
-#include "frame.h"
-#include "game.h"
-#include "inventory.h"
-#include "inventory_dialog.h"
-#include "item.h"
-#include "maths.h"
-#include "painter.h"
-#include "texture.h"
-#include "window.h"
-
-#define ITEM_SIZE       64
-#define ITEM_PADDING    32
-
-#define GRID_WIDTH      ((INVENTORY_COLS_MAX * ITEM_SIZE) +             \
-                        ((INVENTORY_COLS_MAX + 1) * ITEM_PADDING))
-#define GRID_HEIGHT     ((INVENTORY_ROWS_MAX * ITEM_SIZE) +             \
-                        ((INVENTORY_ROWS_MAX + 1) * ITEM_PADDING))
-
-#define LABEL_WIDTH     (GRID_WIDTH)
-#define LABEL_HEIGHT    (64)
-
-#define BUTTON_HEIGHT   (32)
-#define BUTTON_WIDTH    ((GRID_WIDTH) / 4)
-
-/*
- * The frame looks like this:
- *
- * +----------------------------+
- * | [] [] [] [] [] [] [] [] [] |
- * | [] [] [] [] [] [] [] [] [] |
- * | [] [] [] [] [] [] [] [] [] |
- * +----------------------------+
- * | Item name                  |
- * | Item description           |
- * +----------------------------+
- *                       [ Sort ]
- *
- * Where space between cells is determined with ITEM_PADDING macro.
- */
-
-static unsigned int
-total_width(void)
-{
-	return GRID_WIDTH;
-}
-
-static unsigned int
-total_height(void)
-{
-	return GRID_HEIGHT + LABEL_HEIGHT + BUTTON_HEIGHT;
-}
-
-static void
-compute_box_position(const struct inventory_dialog *dlg, int r, int c, int *x, int *y)
-{
-	assert(dlg);
-	assert(x);
-	assert(y);
-
-	*x = dlg->fgrid.x + ((c * ITEM_SIZE) + ((c + 1) * ITEM_PADDING));
-	*y = dlg->fgrid.y + ((r * ITEM_SIZE) + ((r + 1) * ITEM_PADDING));
-}
-
-static void
-draw_grid_item_frame(int x, int y)
-{
-	struct frame frame = {
-		.x = x,
-		.y = y,
-		.w = ITEM_SIZE,
-		.h = ITEM_SIZE,
-		.style = FRAME_STYLE_BOX
-	};
-
-	frame_draw(&frame);
-}
-
-static void
-draw_grid_item_icon(struct item *item, int x, int y)
-{
-	struct texture *icon = item->icon;
-
-	texture_scale(icon, 0, 0, icon->w, icon->h, x, y, ITEM_SIZE, ITEM_SIZE, 0.0);
-}
-
-static void
-draw_grid_item_amount(struct inventory_slot *slot, int x, int y)
-{
-	char nstr[16];
-	struct label label = {
-		.text = nstr,
-		.x = x,
-		.y = y
-	};
-
-	/* Draw the number of item in this slot. */
-	snprintf(nstr, sizeof (nstr), "%d", slot->amount);
-	label_draw(&label);
-}
-
-static void
-draw_grid_item(struct inventory_slot *slot, int x, int y, bool selected)
-{
-	draw_grid_item_frame(x, y);
-
-	if (slot->item) {
-		draw_grid_item_icon(slot->item, x, y);
-		draw_grid_item_amount(slot, x, y);
-	}
-
-	if (selected) {
-		x -= 16;
-		y += (ITEM_SIZE / 2) - 4;
-		painter_draw_circle(x, y, 8);
-	}
-}
-
-static void
-draw_grid_items(const struct inventory_dialog *dlg)
-{
-	int x, y;
-	bool selected;
-
-	for (unsigned int r = 0; r < INVENTORY_ROWS_MAX; ++r) {
-		for (unsigned int c = 0; c < INVENTORY_COLS_MAX; ++c) {
-			selected = r == dlg->selrow && c == dlg->selcol;
-			compute_box_position(dlg, r, c, &x, &y);
-			draw_grid_item(&dlg->inv->items[r][c], x, y, selected);
-		}
-	}
-}
-
-static void
-draw_label(struct inventory_dialog *dlg)
-{
-	assert(dlg);
-
-	struct item *item = dlg->inv->items[dlg->selrow][dlg->selcol].item;
-
-	frame_draw(&dlg->fname);
-	frame_draw(&dlg->fdesc);
-
-	if (item) {
-		dlg->lname.text = item->name;
-		dlg->ldesc.text = item->summary;
-		label_draw(&dlg->lname);
-		label_draw(&dlg->ldesc);
-	}
-}
-
-void
-inventory_dialog_open(struct inventory_dialog *dlg)
-{
-	assert(dlg);
-	assert(dlg->inv);
-
-	int tw, th;
-
-	dlg->state = INVENTORY_DIALOG_SHOWING;
-
-	tw = total_width();
-	th = total_height();
-
-	/* Grid frame position. */
-	dlg->fgrid.w = GRID_WIDTH;
-	dlg->fgrid.h = GRID_HEIGHT;
-	dlg->fgrid.x = dlg->x;
-	dlg->fgrid.y = dlg->y;
-
-	/* Name label. */
-	dlg->fname.w = dlg->lname.w = LABEL_WIDTH;
-	dlg->fname.h = dlg->lname.h = LABEL_HEIGHT / 2;
-	dlg->fname.x = dlg->lname.x = dlg->x;
-	dlg->fname.y = dlg->lname.y = dlg->y + GRID_HEIGHT;
-	dlg->lname.x += ITEM_PADDING;
-	dlg->lname.align = LABEL_ALIGN_LEFT;
-
-	/* Description label. */
-	dlg->fdesc.w = dlg->ldesc.w = LABEL_WIDTH;
-	dlg->fdesc.h = dlg->ldesc.h = LABEL_HEIGHT / 2;
-	dlg->fdesc.x = dlg->ldesc.x = dlg->y;
-	dlg->fdesc.y = dlg->ldesc.y = dlg->y + GRID_HEIGHT + (LABEL_HEIGHT / 2);
-	dlg->ldesc.x += ITEM_PADDING;
-	dlg->ldesc.align = LABEL_ALIGN_LEFT;
-
-	/* Button sort. */
-	dlg->bsort.x = dlg->x;
-	dlg->bsort.y = dlg->y + GRID_HEIGHT + LABEL_HEIGHT;
-	dlg->bsort.w = BUTTON_WIDTH;
-	dlg->bsort.h = BUTTON_HEIGHT;
-	dlg->bsort.text = "Sort";
-}
-
-static void
-handle_keydown(struct inventory_dialog *dlg, const struct event_key *ev)
-{
-	assert(ev && ev->type == EVENT_KEYDOWN);
-
-	switch (ev->key) {
-	case KEY_LEFT:
-		if (dlg->selcol == 0)
-			dlg->selcol = INVENTORY_COLS_MAX - 1;
-		else
-			dlg->selcol--;
-		break;
-	case KEY_RIGHT:
-		dlg->selcol = (dlg->selcol + 1) % INVENTORY_COLS_MAX;
-		break;
-	case KEY_UP:
-		if (dlg->selrow == 0)
-			dlg->selrow = INVENTORY_ROWS_MAX - 1;
-		else
-			dlg->selrow--;
-		break;
-	case KEY_DOWN:
-		dlg->selrow = (dlg->selrow + 1) % INVENTORY_ROWS_MAX;
-		break;
-	default:
-		break;
-	}
-}
-
-static void
-handle_clickdown(struct inventory_dialog *dlg, const struct event_click *ev)
-{
-	assert(dlg);
-	assert(ev && ev->type == EVENT_CLICKDOWN);
-
-	int x, y;
-
-	for (int r = 0; r < INVENTORY_ROWS_MAX; ++r) {
-		for (int c = 0; c < INVENTORY_COLS_MAX; ++c) {
-			compute_box_position(dlg, r, c, &x, &y);
-
-			if (maths_is_boxed(x, y, ITEM_SIZE, ITEM_SIZE, ev->x, ev->y)) {
-				dlg->selrow = r;
-				dlg->selcol = c;
-			}
-		}
-	}
-}
-
-void
-inventory_dialog_handle(struct inventory_dialog *dlg, const union event *event)
-{
-	assert(event);
-
-	switch (event->type) {
-	case EVENT_KEYDOWN:
-		handle_keydown(dlg, &event->key);
-		break;
-	case EVENT_CLICKDOWN:
-		handle_clickdown(dlg, &event->click);
-		break;
-	default:
-		break;
-	}
-
-	button_handle(&dlg->bsort, event);
-
-	if (dlg->bsort.state == BUTTON_STATE_ACTIVATED) {
-		inventory_sort(dlg->inv);
-		button_reset(&dlg->bsort);
-	}
-}
-
-void
-inventory_dialog_update(struct inventory_dialog *dlg, unsigned int ticks)
-{
-	assert(dlg);
-
-	(void)ticks;
-}
-
-void
-inventory_dialog_move(struct inventory_dialog *dlg, int x, int y)
-{
-	assert(dlg);
-
-	dlg->x = x;
-	dlg->y = y;
-}
-
-void
-inventory_dialog_draw(struct inventory_dialog *dlg)
-{
-	assert(dlg);
-
-	frame_draw(&dlg->fgrid);
-	draw_grid_items(dlg);
-	draw_label(dlg);
-	button_draw(&dlg->bsort);
-}
-
-void
-inventory_dialog_finish(struct inventory_dialog *dlg)
-{
-	assert(dlg);
-
-	dlg->state = INVENTORY_DIALOG_NONE;
-}
--- a/libcore/core/inventory_dialog.h	Thu Oct 15 09:21:04 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,76 +0,0 @@
-/*
- * inventory_dialog.h -- dialog for items
- *
- * Copyright (c) 2020 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef INVENTORY_DIALOG_H
-#define INVENTORY_DIALOG_H
-
-#include "button.h"
-#include "label.h"
-#include "frame.h"
-
-union event;
-
-struct inventory;
-struct theme;
-
-/**
- * \brief Inventory dialog state.
- */
-enum inventory_dialog_state {
-	INVENTORY_DIALOG_NONE,
-	INVENTORY_DIALOG_SHOWING
-};
-
-/**
- * \brief Inventory dialog.
- */
-struct inventory_dialog {
-	int x;                                  /*!< (+) Position in x. */
-	int y;                                  /*!< (+) Position in y. */
-	struct inventory *inv;                  /*!< (+&) Inventory to use. */
-	struct theme *theme;                    /*!< (+&?) Theme to use. */
-	struct button bsort;                    /*!< (-) Button sort. */
-	struct frame fgrid;                     /*!< (-) Grid frame. */
-	struct frame fname;                     /*!< (-) Frame for name. */
-	struct frame fdesc;                     /*!< (-) Frame for description. */
-	struct label lname;                     /*!< (-) Label for name. */
-	struct label ldesc;                     /*!< (-) Label for description. */
-	enum inventory_dialog_state state;      /*!< (-) Current dialog state. */
-	unsigned int selrow;                    /*!< (-) Current selected row. */
-	unsigned int selcol;                    /*!< (-) Current selected column. */
-};
-
-void
-inventory_dialog_open(struct inventory_dialog *dlg);
-
-void
-inventory_dialog_handle(struct inventory_dialog *dlg, const union event *event);
-
-void
-inventory_dialog_update(struct inventory_dialog *dlg, unsigned int ticks);
-
-void
-inventory_dialog_move(struct inventory_dialog *dlg, int x, int y);
-
-void
-inventory_dialog_draw(struct inventory_dialog *dlg);
-
-void
-inventory_dialog_finish(struct inventory_dialog *dlg);
-
-#endif /* !INVENTORY_DIALOG_H */
--- a/libcore/core/item.h	Thu Oct 15 09:21:04 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,57 +0,0 @@
-/*
- * item.h -- inventory items
- *
- * Copyright (c) 2020 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef MOLKO_ITEM_H
-#define MOLKO_ITEM_H
-
-/**
- * \file item.h
- * \brief Inventory items.
- */
-
-#include <stdbool.h>
-
-/**
- * \brief Maximum count of an item into a stack.
- */
-#define ITEM_STACK_MAX  64
-
-struct character;
-struct texture;
-
-/**
- * \brief Inventory items.
- */
-struct item {
-	const char *name;               /*!< (+) Name of item. */
-	const char *summary;            /*!< (+) Summary description. */
-	struct texture *icon;           /*!< (+&) Icon to show. */
-	unsigned int stackable;         /*!< (+) Stack count allowed. */
-
-	/**
-	 * (+) Execute the action for this character.
-	 */
-	void (*exec)(const struct item *, struct character *);
-
-	/**
-	 * (+?) Tells if the item can be used in this context.
-	 */
-	bool (*allowed)(const struct item *, const struct character *);
-};
-
-#endif /* !MOLKO_ITEM_H */
--- a/libcore/core/label.c	Thu Oct 15 09:21:04 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,35 +0,0 @@
-/*
- * label.c -- GUI label
- *
- * Copyright (c) 2020 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <assert.h>
-
-#include "label.h"
-#include "theme.h"
-#include "trace.h"
-
-void
-label_draw(const struct label *label)
-{
-	assert(label);
-	assert(label->text);
-
-	if (label->w == 0 || label->h == 0)
-		trace("label %p has null dimensions", label);
-
-	theme_draw_label(label->theme, label);
-}
--- a/libcore/core/label.h	Thu Oct 15 09:21:04 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,87 +0,0 @@
-/*
- * label.h -- GUI label
- *
- * Copyright (c) 2020 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef MOLKO_LABEL_H
-#define MOLKO_LABEL_H
-
-/**
- * \file label.h
- * \brief GUI label.
- */
-
-struct theme;
-
-/**
- * \brief Label flags.
- */
-enum label_flags {
-	LABEL_FLAGS_NONE,                       /*!< No flags. */
-	LABEL_FLAGS_SHADOW      = (1 << 0),     /*!< Enable shadow. */
-};
-
-/**
- * \brief Label alignment in bounding box.
- *
- * The alignment is described as following:
- *
- * ```
- * +---------------------+
- * | 1        2        3 |
- * |                     |
- * | 8        0        4 |
- * |                     |
- * | 7        6        5 |
- * +---------------------+
- * ```
- */
-enum label_align {
-	LABEL_ALIGN_CENTER,             /*!< Align to the center (default). */
-	LABEL_ALIGN_TOP_LEFT,           /*!< Top left. */
-	LABEL_ALIGN_TOP,                /*!< Top center (aligned horizontally). */
-	LABEL_ALIGN_TOP_RIGHT,          /*!< Top right. */
-	LABEL_ALIGN_RIGHT,              /*!< Right (aligned vertically). */
-	LABEL_ALIGN_BOTTOM_RIGHT,       /*!< Bottom right. */
-	LABEL_ALIGN_BOTTOM,             /*!< Bottom (aligned horizontally). */
-	LABEL_ALIGN_BOTTOM_LEFT,        /*!< Bottom left. */
-	LABEL_ALIGN_LEFT                /*!< Left (aligned vertically). */
-};
-
-/**
- * \brief GUI label.
- */
-struct label {
-	int x;                          /*!< (+) Position in x. */
-	int y;                          /*!< (+) Position in y. */
-	unsigned int w;                 /*!< (+) Width. */
-	unsigned int h;                 /*!< (+) Height. */
-	const char *text;               /*!< (+&) Text to show. */
-	enum label_flags flags;         /*!< (+) Optional flags. */
-	enum label_align align;         /*!< (+) How to positionate label. */
-	struct theme *theme;            /*!< (+&?) Theme to use. */
-};
-
-/**
- * Draw the label.
- *
- * \pre label != NULL
- * \param label the label to draw
- */
-void
-label_draw(const struct label *label);
-
-#endif /* !MOLKO_LABEL_H */
--- a/libcore/core/map.c	Thu Oct 15 09:21:04 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,237 +0,0 @@
-/*
- * map.c -- game map
- *
- * Copyright (c) 2020 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <assert.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "error.h"
-#include "error_p.h"
-#include "image.h"
-#include "map.h"
-#include "painter.h"
-#include "rbuf.h"
-#include "sprite.h"
-#include "sys.h"
-#include "texture.h"
-#include "window.h"
-
-/* Create %<v>c string literal for scanf */
-#define MAX_F(v) MAX_F_(v)
-#define MAX_F_(v) "%" #v "c"
-
-static void
-parse_layer(struct map_data *data, const char *line, FILE *fp)
-{
-	char layer_name[32 + 1] = { 0 };
-	struct map_layer *layer;
-	size_t amount, current;
-
-	/* Determine layer. */
-	if (sscanf(line, "layer|%32s", layer_name) <= 0)
-		return;
-	if (strcmp(layer_name, "background") == 0)
-		layer = &data->layers[0];
-	else if (strcmp(layer_name, "foreground") == 0)
-		layer = &data->layers[1];
-	else
-		return;
-
-	/* Check if weight/height has been specified. */
-	if (data->w == 0 || data->h == 0)
-		return;
-
-	amount = data->w * data->h;
-	current = 0;
-
-	if (!(layer->tiles = calloc(amount, sizeof (unsigned short))))
-		return;
-
-	for (int tile; fscanf(fp, "%d", &tile) && current < amount; ++current)
-		layer->tiles[current] = tile;
-}
-
-static void
-parse(struct map_data *data, const char *line, FILE *fp)
-{
-	if (strncmp(line, "title", 5) == 0)
-		sscanf(line, "title|" MAX_F(MAP_TITLE_MAX), data->title);
-	else if (strncmp(line, "width", 5) == 0)
-		sscanf(line, "width|%u", &data->w);
-	else if (strncmp(line, "height", 6) == 0)
-		sscanf(line, "height|%u", &data->h);
-	else if (strncmp(line, "tilewidth", 9) == 0)
-		sscanf(line, "tilewidth|%hu", &data->tile_w);
-	else if (strncmp(line, "tileheight", 10) == 0)
-		sscanf(line, "tileheight|%hu", &data->tile_h);
-	else if (strncmp(line, "origin", 6) == 0)
-		sscanf(line, "origin|%d|%d", &data->origin_x, &data->origin_y);
-	else if (strncmp(line, "tileset", 7) == 0)
-		sscanf(line, "tileset|" MAX_F(MAP_TILESET_MAX), data->tileset);
-	else if (strncmp(line, "layer", 5) == 0)
-		parse_layer(data, line, fp);
-}
-
-static bool
-check(struct map_data *data)
-{
-	if (strlen(data->title) == 0)
-		return error_printf("data has no title");
-	if (data->w == 0 || data->h == 0)
-		return error_printf("data has null sizes");
-	if (data->tile_w == 0 || data->tile_h == 0)
-		return error_printf("data has null tile sizes");
-	if (!data->layers[0].tiles || !data->layers[1].tiles)
-		return error_printf("could not allocate data");
-
-	return true;
-}
-
-static void
-draw_layer(struct map *map, const struct map_layer *layer)
-{
-	assert(map);
-	assert(layer);
-
-	struct sprite sprite;
-	int x = 0, y = 0;
-
-	sprite_init(&sprite, map->tileset, map->data->tile_w, map->data->tile_h);
-
-	for (unsigned int r = 0; r < map->data->w; ++r) {
-		for (unsigned int c = 0; c < map->data->h; ++c) {
-			unsigned int si = r * map->data->w + c;
-			unsigned int sr = (layer->tiles[si] - 1) / sprite.ncols;
-			unsigned int sc = (layer->tiles[si] - 1) % sprite.nrows;
-
-			if (layer->tiles[si] != 0)
-				sprite_draw(&sprite, sr, sc, x, y);
-
-			x += map->data->tile_w;
-		}
-
-		x = 0;
-		y += map->data->tile_h;
-	}
-}
-
-bool
-map_data_open_fp(struct map_data *data, FILE *fp)
-{
-	assert(data);
-
-	char line[1024];
-
-	if (!fp)
-		return false;
-
-	memset(data, 0, sizeof (*data));
-
-	while (fgets(line, sizeof (line), fp)) {
-		/* Remove \n if any */
-		line[strcspn(line, "\n")] = '\0';
-		parse(data, line, fp);
-	}
-
-	fclose(fp);
-
-	if (!check(data)) {
-		map_data_finish(data);
-		return false;
-	}
-
-	/* Compute real size. */
-	data->real_w = data->w * data->tile_w;
-	data->real_h = data->h * data->tile_h;
-
-	return true;
-}
-
-bool
-map_data_open(struct map_data *data, const char *path)
-{
-	assert(data);
-	assert(path);
-
-	return map_data_open_fp(data, fopen(path, "r"));
-}
-
-bool
-map_data_openmem(struct map_data *data, const void *buf, size_t bufsz)
-{
-	assert(data);
-	assert(buf);
-
-	return map_data_open_fp(data, fmemopen((void *)buf, bufsz, "r"));
-}
-
-void
-map_data_finish(struct map_data *data)
-{
-	assert(data);
-
-	free(data->layers[0].tiles);
-	free(data->layers[1].tiles);
-
-	memset(data, 0, sizeof (*data));
-}
-
-bool
-map_init(struct map *map, struct map_data *data, struct texture *tileset)
-{
-	assert(map);
-	assert(data);
-	assert(tileset && texture_ok(tileset));
-
-	if (!(texture_new(&map->picture, data->real_w, data->real_h)))
-		return false;
-
-	map->data = data;
-	map->tileset = tileset;
-
-	map_repaint(map);
-
-	return true;
-}
-
-void
-map_draw(struct map *map, int srcx, int srcy)
-{
-	texture_scale(&map->picture, srcx, srcy, window.w, window.h,
-	    0, 0, window.w, window.h, 0.0);
-}
-
-void
-map_repaint(struct map *map)
-{
-	PAINTER_BEGIN(&map->picture);
-	draw_layer(map, &map->data->layers[0]);
-	draw_layer(map, &map->data->layers[1]);
-	PAINTER_END();
-}
-
-void
-map_finish(struct map *map)
-{
-	assert(map);
-
-	texture_finish(&map->picture);
-
-	memset(map, 0, sizeof (*map));
-}
--- a/libcore/core/map.h	Thu Oct 15 09:21:04 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,159 +0,0 @@
-/*
- * map.h -- game map
- *
- * Copyright (c) 2020 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef MOLKO_MAP_H
-#define MOLKO_MAP_H
-
-/**
- * \file map.h
- * \brief Game map.
- */
-
-#include <stdbool.h>
-#include <stdio.h>
-
-#include "texture.h"
-
-/**
- * \brief Max title length for a map.
- */
-#define MAP_TITLE_MAX   32
-
-/**
- * \brief Max filename for tilesets.
- */
-#define MAP_TILESET_MAX FILENAME_MAX
-
-/**
- * \brief Map layer.
- */
-struct map_layer {
-	unsigned short *tiles;          /*!< (+) Array of tiles, depending on the map size. */
-};
-
-/**
- * \brief Map definition structure.
- *
- * This structure only defines the map characteristics. It does not have any
- * logic and is left for game state.
- */
-struct map_data {
-	char title[MAP_TITLE_MAX];      /*!< (+) The map title. */
-	char tileset[MAP_TILESET_MAX];  /*!< (+) Name of tileset to use. */
-	int origin_x;                   /*!< (+) Where the player starts in X. */
-	int origin_y;                   /*!< (+) Where the player starts in Y. */
-	unsigned int real_w;            /*!< (-) Real width in pixels. */
-	unsigned int real_h;            /*!< (-) Real height in pixels. */
-	unsigned int w;                 /*!< (-) Map width in cells. */
-	unsigned int h;                 /*!< (-) Map height in cells. */
-	unsigned short tile_w;          /*!< (-) Pixels per cell (width). */
-	unsigned short tile_h;          /*!< (-) Pixels per cell (height). */
-	struct map_layer layers[2];     /*!< (+) Layers (background, foreground). */
-};
-
-/**
- * \brief High level map object.
- *
- * This structure reference a map and perform drawing operations.
- */
-struct map {
-	struct map_data *data;          /*!< (+&) Map data. */
-	struct texture *tileset;        /*!< (+&) Tileset to use. */
-	struct texture picture;         /*!< (-) Map drawn into a picture. */
-};
-
-/**
- * Open a map defintion.
- *
- * \pre data != NULL
- * \pre path != NULL
- * \param data the map defintion to fill
- * \param path the path to the map
- * \return True if successfully loaded.
- */
-bool
-map_data_open(struct map_data *data, const char *path);
-
-/**
- * Open map data definition from memory.
- *
- *\pre data != NULL
- *\pre buf != NULL
- *\param data the map definition to fill
- *\param buf the source buffer
- *\param bufsz the source buffer size
- */
-bool
-map_data_openmem(struct map_data *data, const void *buf, size_t bufsz);
-
-/**
- * Dispose the map definition data.
- *
- * \pre data != NULL
- * \param data the map definition
- */
-void
-map_data_finish(struct map_data *data);
-
-/**
- * Initialize this map.
- *
- * \pre map != NULL
- * \pre data != NULL
- * \pre tileset != NULL && texture_ok(tileset)
- * \param map the map to initialize
- * \param data the definition to reference
- * \param tileset the tileset to use
- * \return False on errors.
- */
-bool
-map_init(struct map *map,
-         struct map_data *data,
-         struct texture *tileset);
-
-/**
- * Render a map.
- *
- * \pre map != NULL
- * \param map the map to render
- * \param srcx the x coordinate region
- * \param srcy the y coordinate region
- */
-void
-map_draw(struct map *map, int srcx, int srcy);
-
-/**
- * Force map repaint on its texture.
- *
- * \pre map != NULL
- * \param map the map to repaint
- * \warning This function does not render anything on the screen.
- */
-void
-map_repaint(struct map *map);
-
-/**
- * Dispose map resources.
- *
- * \pre map != NULL
- * \param map the map to close
- */
-void
-map_finish(struct map *map);
-
-#endif /* !MOLKO_MAP_H */
--- a/libcore/core/map_state.c	Thu Oct 15 09:21:04 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,350 +0,0 @@
-/*
- * map_state.c -- state when player is on a map
- *
- * Copyright (c) 2020 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <stdio.h>
-
-#include "debug.h"
-#include "event.h"
-#include "game.h"
-#include "map.h"
-#include "map_state.h"
-#include "painter.h"
-#include "state.h"
-#include "texture.h"
-#include "walksprite.h"
-#include "window.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    80
-#define MARGIN_HEIGHT   80
-
-/*
- * Convenient macros to access the state data.
- */
-#define MAP()           (&map_state_data.map)
-#define PLAYER()        (&map_state_data.player)
-#define VIEW()          (&map_state_data.view)
-
-/*
- * 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
-};
-
-/*
- * Additional data that is not necessary to expose in map_state_data.
- */
-static struct {
-	struct {
-		enum movement moving;
-		struct walksprite ws;
-	} player;
-
-	struct {
-		int x;
-		int y;
-		unsigned int w;
-		unsigned int h;
-	} margin;
-} cache;
-
-static void
-center(void)
-{
-	VIEW()->x = PLAYER()->x - (VIEW()->w / 2);
-	VIEW()->y = PLAYER()->y - (VIEW()->h / 2);
-
-	if (VIEW()->x < 0)
-		VIEW()->x = 0;
-	else if ((unsigned int)VIEW()->x > MAP()->data.real_w - VIEW()->w)
-		VIEW()->x = MAP()->data.real_w - VIEW()->w;
-
-	if (VIEW()->y < 0)
-		VIEW()->y = 0;
-	else if ((unsigned int)VIEW()->y > MAP()->data.real_h - VIEW()->h)
-		VIEW()->y = MAP()->data.real_h - VIEW()->h;
-}
-
-static void
-enter(void)
-{
-	/* Adjust map properties. */
-	struct map *m = &map_state_data.map.map;
-
-	map_repaint(m);
-	MAP()->data.real_w = m->picture.w;
-	MAP()->data.real_h = m->picture.h;
-
-	/* Adjust view. */
-	VIEW()->w = window.w;
-	VIEW()->h = window.h;
-
-	/* Adjust margin. */
-	cache.margin.w = VIEW()->w - (MARGIN_WIDTH * 2);
-	cache.margin.h = VIEW()->h - (MARGIN_HEIGHT * 2);
-
-	/* Center the view by default. */
-	center();
-
-	/* Final bits. */
-	walksprite_init(&cache.player.ws, &PLAYER()->sprite, 300);
-}
-
-static void
-leave(void)
-{
-}
-
-static void
-handle_keydown(const union event *event)
-{
-	switch (event->key.key) {
-	case KEY_UP:
-		cache.player.moving |= MOVING_UP;
-		break;
-	case KEY_RIGHT:
-		cache.player.moving |= MOVING_RIGHT;
-		break;
-	case KEY_DOWN:
-		cache.player.moving |= MOVING_DOWN;
-		break;
-	case KEY_LEFT:
-		cache.player.moving |= MOVING_LEFT;
-		break;
-	default:
-		break;
-	}
-
-	PLAYER()->angle = orientations[cache.player.moving];
-}
-
-static void
-handle_keyup(const union event *event)
-{
-	switch (event->key.key) {
-	case KEY_UP:
-		cache.player.moving &= ~(MOVING_UP);
-		break;
-	case KEY_RIGHT:
-		cache.player.moving &= ~(MOVING_RIGHT);
-		break;
-	case KEY_DOWN:
-		cache.player.moving &= ~(MOVING_DOWN);
-		break;
-	case KEY_LEFT:
-		cache.player.moving &= ~(MOVING_LEFT);
-		break;
-	default:
-		break;
-	}
-}
-
-static void
-move_right(unsigned int delta)
-{
-	PLAYER()->x += delta;
-
-	if (PLAYER()->x > (int)(cache.margin.x + cache.margin.w)) {
-		VIEW()->x = (PLAYER()->x - VIEW()->w) + MARGIN_WIDTH;
-
-		if (VIEW()->x >= (int)(MAP()->data.real_w - VIEW()->w))
-			VIEW()->x = MAP()->data.real_w - VIEW()->w;
-	}
-
-	if (PLAYER()->x > (int)MAP()->data.real_w - 48)
-		PLAYER()->x = MAP()->data.real_w - 48;
-}
-
-static void
-move_left(unsigned int delta)
-{
-	PLAYER()->x -= delta;
-
-	if (PLAYER()->x < cache.margin.x) {
-		VIEW()->x = PLAYER()->x - MARGIN_WIDTH;
-
-		if (VIEW()->x < 0)
-			VIEW()->x = 0;
-	}
-
-	if (PLAYER()->x < 0)
-		PLAYER()->x = 0;
-}
-
-static void
-move_down(unsigned int delta)
-{
-	PLAYER()->y += delta;
-
-	if (PLAYER()->y > (int)(cache.margin.y + cache.margin.h)) {
-		VIEW()->y = (PLAYER()->y - VIEW()->h) + MARGIN_HEIGHT;
-
-		if (VIEW()->y >= (int)(MAP()->data.real_h - VIEW()->h))
-			VIEW()->y = MAP()->data.real_h - VIEW()->h;
-	}
-
-	if (PLAYER()->y > (int)MAP()->data.real_h - 48)
-		PLAYER()->y = MAP()->data.real_h - 48;
-}
-
-static void
-move_up(unsigned int delta)
-{
-	PLAYER()->y -= delta;
-
-	if (PLAYER()->y < cache.margin.y) {
-		VIEW()->y = PLAYER()->y - MARGIN_HEIGHT;
-
-		if (VIEW()->y < 0)
-			VIEW()->y = 0;
-	}
-
-	if (PLAYER()->y < 0)
-		PLAYER()->y = 0;
-}
-
-static void
-move(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. */
-	cache.margin.x = VIEW()->x + MARGIN_WIDTH;
-	cache.margin.y = VIEW()->y + MARGIN_HEIGHT;
-
-	int dx = 0;
-	int dy = 0;
-
-	if (cache.player.moving == 0)
-		return;
-
-	if (cache.player.moving & MOVING_UP)
-		dy = -1;
-	if (cache.player.moving & MOVING_DOWN)
-		dy = 1;
-	if (cache.player.moving & MOVING_LEFT)
-		dx = -1;
-	if (cache.player.moving & MOVING_RIGHT)
-		dx = 1;
-
-	/* Move the player and adjust view if needed. */
-	if (dx > 0)
-		move_right(delta);
-	else if (dx < 0)
-		move_left(delta);
-
-	if (dy > 0)
-		move_down(delta);
-	else if (dy < 0)
-		move_up(delta);
-
-	walksprite_update(&cache.player.ws, ticks);
-}
-
-static void
-handle(const union event *event)
-{
-	switch (event->type) {
-	case EVENT_KEYDOWN:
-		handle_keydown(event);
-		break;
-	case EVENT_KEYUP:
-		handle_keyup(event);
-		break;
-	default:
-		break;
-	}
-}
-
-static void
-update(unsigned int ticks)
-{
-	move(ticks);
-}
-
-static void
-draw(void)
-{
-	struct debug_report report = {0};
-
-	map_draw(&map_state_data.map.map, VIEW()->x, VIEW()->y);
-	walksprite_draw(
-		&cache.player.ws,
-		PLAYER()->angle,
-		PLAYER()->x - VIEW()->x,
-		PLAYER()->y - VIEW()->y);
-
-	debug_printf(&report, "position: %d, %d", PLAYER()->x,
-	    PLAYER()->y);
-	debug_printf(&report, "view: %d, %d", VIEW()->x,
-	    VIEW()->y);
-}
-
-struct map_state_data map_state_data;
-
-struct state map_state = {
-	.enter = enter,
-	.leave = leave,
-	.update = update,
-	.handle = handle,
-	.draw = draw
-};
--- a/libcore/core/map_state.h	Thu Oct 15 09:21:04 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,74 +0,0 @@
-/*
- * map_state.h -- state when player is on a map
- *
- * Copyright (c) 2020 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef MOLKO_MAP_STATE_H
-#define MOLKO_MAP_STATE_H
-
-/**
- * \file map_state.h
- * \brief State when player is on a map.
- * \ingroup states
- */
-
-#include "map.h"
-#include "sprite.h"
-
-/**
- * \brief Data for the state.
- *
- * Update this structure before switching to this state.
- */
-extern struct map_state_data {
-	/**
-	 * Map properties.
-	 */
-	struct {
-		struct map_data data;   /*!< (+) Map data. */
-		struct map map;         /*!< (+) Map object. */
-	} map;
-
-	/**
-	 * Player position.
-	 *
-	 * If you adjust this structure, it is strictly encouraged to update
-	 * the view as well.
-	 */
-	struct {
-		struct sprite sprite;   /*!< (+) The sprite to use */
-		int x;                  /*!< (+) Player position in x */
-		int y;                  /*!< (+) Player position in y */
-		int angle;              /*!< (+) Player angle (see walksprite) */
-	} player;
-
-	/**
-	 * Position and size of the view.
-	 */
-	struct {
-		int x;                  /*!< (+) Position in x */
-		int y;                  /*!< (+) Position in y */
-		unsigned int w;         /*!< (+) View width */
-		unsigned int h;         /*!< (+) View height */
-	} view;
-} map_state_data; /*!< Access to data. */
-
-/**
- * \brief State when player is on a map.
- */
-extern struct state map_state;
-
-#endif /* !MOLKO_MAP_STATE_H */
--- a/libcore/core/message.c	Thu Oct 15 09:21:04 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,270 +0,0 @@
-/*
- * message.c -- message dialog
- *
- * Copyright (c) 2020 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <assert.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "action.h"
-#include "event.h"
-#include "font.h"
-#include "frame.h"
-#include "label.h"
-#include "maths.h"
-#include "message.h"
-#include "painter.h"
-#include "panic.h"
-#include "sprite.h"
-#include "texture.h"
-#include "theme.h"
-#include "trace.h"
-#include "util.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 bool
-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 void
-draw_lines(const struct message *msg)
-{
-	struct theme theme;
-	unsigned int lineh;
-
-	/* Shallow copy theme to modify colors. */
-	theme_shallow(&theme, msg->theme);
-
-	/* Compute text size for list alignment. */
-	lineh = font_height(theme.fonts[THEME_FONT_INTERFACE]);
-
-	for (int i = 0; i < 6; ++i) {
-		if (!msg->text[i])
-			continue;
-
-		struct label label = {
-			.y = i * lineh,
-			.w = msg->w,
-			.h = msg->h,
-			.theme = &theme,
-			.text = msg->text[i],
-			.align = LABEL_ALIGN_TOP_LEFT,
-			.flags = LABEL_FLAGS_SHADOW
-		};
-
-		/*
-		 * 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)
-			theme.colors[THEME_COLOR_NORMAL] = THEME(msg)->colors[THEME_COLOR_SELECTED];
-		else
-			theme.colors[THEME_COLOR_NORMAL] = THEME(msg)->colors[THEME_COLOR_NORMAL];
-
-		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)
-		trace("message is automatic but has zero timeout");
-}
-
-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 < 5 && msg->text[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;
-	}
-}
-
-bool
-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(struct message *msg)
-{
-	assert(msg);
-
-	struct texture tex;
-	int x, y;
-	unsigned int w, h;
-
-	if (!texture_new(&tex, msg->w, msg->h))
-		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. */
-	maths_centerize(&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/libcore/core/message.h	Thu Oct 15 09:21:04 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,197 +0,0 @@
-/*
- * message.h -- message dialog
- *
- * Copyright (c) 2020 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef MOLKO_MESSAGE_H
-#define MOLKO_MESSAGE_H
-
-/**
- * \file message.h
- * \brief Message dialog.
- * \ingroup actions
- * \ingroup drawing
- *
- * This module's purpose is to show a dialog box into the screen to show text
- * and optionally ask the user a question.
- *
- * By itself, it is very low level and does not prevent other parts of the game
- * to use the input so you probably need to inhibit input if your dialog is
- * meant to be displayed on a map.
- *
- * To use it use the following procedure:
- *
- * 1. Create a struct message object and set required properties,
- * 2. Call \ref message_start to reset the state,
- * 3. Call \ref message_handle and \ref message_update with appropriate values,
- * 4. Call \ref message_draw to render the dialog.
- *
- * Depending on message flags or user input, step 3 may return true in this
- * case you should stop using the message as it has completed rendering.
- *
- * \note All properties must exist until the object is no longer used.
- *
- * \code
- * struct message msg = {
- * 	// You can show up to 6 lines.
- * 	.text = {
- * 		"Hello, what's up?"
- * 	},
- * 	// This image will be shown on the left as user face.
- * 	.avatar = mysuperavatar,
- * 	// This should point to a image that is used as background.
- * 	.frame = mysuperframe,
- * 	// The first color is normal text, the second is for selected text
- * 	// in case of question.
- * 	.colors = { 0xffffffff, 0x0000ffff },
- * 	// This indicates this message is a question.
- * 	.flags = MESSAGE_QUESTION
- * };
- * \endcode
- */
-
-#include <stdbool.h>
-
-#include "texture.h"
-
-struct action;
-struct font;
-struct theme;
-
-union event;
-
-/**
- * \brief Default animation speed in milliseconds.
- */
-#define MESSAGE_DELAY_DEFAULT   (150)
-
-/**
- * \brief Default timeout in milliseconds for automatic messages.
- */
-#define MESSAGE_TIMEOUT_DEFAULT (5000)
-
-/**
- * \brief Message flags.
- */
-enum message_flags {
-	MESSAGE_FLAGS_AUTOMATIC         = (1 << 0),     /*!< Will automatically change state by itself. */
-	MESSAGE_FLAGS_QUESTION          = (1 << 1),     /*!< The message is a question. */
-	MESSAGE_FLAGS_FADEIN            = (1 << 2),     /*!< Animate opening. */
-	MESSAGE_FLAGS_FADEOUT           = (1 << 3)      /*!< Animate closing. */
-};
-
-/**
- * \brief Message state.
- */
-enum message_state {
-	MESSAGE_STATE_NONE,             /*!< Message hasn't start yet or is finished */
-	MESSAGE_STATE_OPENING,          /*!< Message animation is opening */
-	MESSAGE_STATE_SHOWING,          /*!< Message is displaying */
-	MESSAGE_STATE_HIDING            /*!< Message animation for hiding */
-};
-
-/**
- * \brief Message object.
- *
- * This structure is used to display a message into the screen. It does not own
- * any user properties and therefore must exist while using it.
- */
-struct message {
-	int x;                          /*!< (+) Position in x. */
-	int y;                          /*!< (+) Position in y. */
-	unsigned int w;                 /*!< (+) Width. */
-	unsigned int h;                 /*!< (+) Height. */
-	unsigned int delay;             /*!< (+) Delay for animations. */
-	unsigned int timeout;           /*!< (+) Timeout in milliseconds. */
-	const char *text[6];            /*!< (+) Lines of text to show. */
-	struct texture *avatar;         /*!< (+&?) Avatar face. */
-	unsigned int index;             /*!< (+) Line selected */
-	enum message_flags flags;       /*!< (+) Message flags */
-	enum message_state state;       /*!< (-) Current state */
-	struct theme *theme;            /*!< (+&?) Theme to use. */
-	unsigned int elapsed;           /*!< (-) Time elapsed. */
-	double scale;                   /*!< (-) Current scale [0-1]. */
-};
-
-/**
- * Start opening the message. This function will reset the message state and
- * elapsed time.
- *
- * \pre msg != NULL
- * \pre msg->delay > 0 if msg->flags contains MESSAGE_FLAGS_FADEIN or
- *      MESSAGE_FLAGS_FADEOUT
- * \param msg the message
- */
-void
-message_start(struct message *msg);
-
-/**
- * Handle input events.
- *
- * This function will alter state of the message and change its selection in
- * case of question.
- *
- * \pre msg != NULL
- * \pre ev != NULL
- * \param msg the message
- * \param ev the event which occured
- */
-void
-message_handle(struct message *msg, const union event *ev);
-
-/**
- * Update the message state and elapsed time..
- *
- * \pre msg != NULL
- * \param msg the message
- * \param ticks the elapsed delay since last frame
- * \return true if it has finished
- */
-bool
-message_update(struct message *msg, unsigned int ticks);
-
-/**
- * Draw the message into the screen.
- *
- * \pre msg != NULL
- * \param msg the message
- */
-void
-message_draw(struct message *msg);
-
-/**
- * Start hiding the message.
- *
- * \pre msg != NULL
- * \param msg the message
- * \note You should still continue to draw the message as the animation is not
- *       finished!
- */
-void
-message_hide(struct message *msg);
-
-/**
- * Convert message into an action.
- *
- * \pre msg != NULL
- * \pre action != NULL
- * \param msg the message to reference
- * \param action the action to fill
- */
-void
-message_action(struct message *msg, struct action *action);
-
-#endif /* !MOLKO_MESSAGE_H */
--- a/libcore/core/theme.c	Thu Oct 15 09:21:04 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,308 +0,0 @@
-/*
- * theme.c -- abstract theming
- *
- * Copyright (c) 2020 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <assert.h>
-#include <stddef.h>
-#include <string.h>
-
-#include "button.h"
-#include "checkbox.h"
-#include "font.h"
-#include "frame.h"
-#include "label.h"
-#include "maths.h"
-#include "painter.h"
-#include "panic.h"
-#include "texture.h"
-#include "theme.h"
-#include "util.h"
-
-#include <core/assets/fonts/f25-bank-printer.h>
-#include <core/assets/fonts/comic-neue.h>
-
-#define THEME(t) (t ? t : &default_theme)
-
-#define CHECKBOX_W 16
-#define CHECKBOX_H 16
-#define CHECKBOX_RAD 6
-
-static void
-box(int x, int y, unsigned int w, unsigned int h)
-{
-	/* Some basic outlines. */
-	painter_set_color(0x4d3533ff);
-
-	painter_draw_line(x, y, x + w, y);
-	painter_draw_line(x, y + h, x + w, y + h);
-	painter_draw_line(x, y, x, y + h);
-	painter_draw_line(x + w, y, x + w, y + h);
-}
-
-static void
-draw_frame(struct theme *t, const struct frame *frame)
-{
-	(void)t;
-
-	if (frame->style == FRAME_STYLE_BOX)
-		painter_set_color(0x6e4c30ff);
-	else
-		painter_set_color(0xce9248ff);
-
-	painter_draw_rectangle(frame->x, frame->y, frame->w, frame->h);
-	box(frame->x, frame->y, frame->w, frame->h);
-}
-
-static void
-draw_label(struct theme *t, const struct label *label)
-{
-	struct font *font;
-	struct texture tex;
-	int x, y, bx, by;
-	unsigned int tw, th, bw, bh;
-
-	/* Compute real box size according to padding. */
-	bx = label->x + t->padding;
-	by = label->y + t->padding;
-	bw = label->w - (t->padding * 2);
-	bh = label->h - (t->padding * 2);
-
-	/* Make a shallow copy of the interface font. */
-	font = t->fonts[THEME_FONT_INTERFACE];
-
-	/* Compute text size. */
-	if (!font_box(font, label->text, &tw, &th))
-		panic();
-
-	/* Compute position according to alignment and box. */
-	switch (label->align) {
-	case LABEL_ALIGN_CENTER:
-		maths_centerize(&x, &y, tw, th, bx, by, bw, bh);
-		break;
-	case LABEL_ALIGN_TOP_LEFT:
-		x = bx;
-		y = by;
-		break;
-	case LABEL_ALIGN_TOP:
-		maths_centerize(&x, NULL, tw, th, bx, by, bw, bh);
-		y = by;
-		break;
-	case LABEL_ALIGN_TOP_RIGHT:
-		x = bx + bw - tw;
-		y = by;
-		break;
-	case LABEL_ALIGN_RIGHT:
-		maths_centerize(NULL, &y, tw, th, bx, by, bw, bh);
-		x = bx + bw - tw;
-		break;
-	case LABEL_ALIGN_BOTTOM_RIGHT:
-		x = bx + bw - tw;
-		y = by + bh - th;
-		break;
-	case LABEL_ALIGN_BOTTOM:
-		maths_centerize(&x, NULL, tw, th, bx, by, bw, bh);
-		y = by + bh - th;
-		break;
-	case LABEL_ALIGN_BOTTOM_LEFT:
-		x = bx;
-		y = by + bh - th;
-		break;
-	case LABEL_ALIGN_LEFT:
-		maths_centerize(NULL, &y, tw, th, bx, by, bw, bh);
-		x = bx;
-	default:
-		break;
-	}
-
-	/* Shadow text, only if enabled. */
-	if (label->flags & LABEL_FLAGS_SHADOW) {
-		font->color = t->colors[THEME_COLOR_SHADOW];
-
-		if (!font_render(font, &tex, label->text))
-			panic();
-
-		texture_draw(&tex, x + 1, y + 1);
-		texture_finish(&tex);
-	}
-
-	/* Normal text. */
-	font->color = t->colors[THEME_COLOR_NORMAL];
-
-	if (!font_render(font, &tex, label->text))
-		panic();
-
-	texture_draw(&tex, x, y);
-	texture_finish(&tex);
-}
-
-static void
-draw_button(struct theme *t, const struct button *button)
-{
-	(void)t;
-
-	struct label label = {
-		.text = button->text,
-		.x = button->x,
-		.y = button->y,
-		.w = button->w,
-		.h = button->h
-	};
-
-	painter_set_color(0xabcdefff);
-	painter_draw_rectangle(button->x, button->y, button->w, button->h);
-
-	label_draw(&label);
-
-	box(button->x, button->y, button->w, button->h);
-}
-
-static void
-draw_checkbox(struct theme *t, const struct checkbox *cb)
-{
-	box(cb->x, cb->y, CHECKBOX_W, CHECKBOX_H);
-
-	if (cb->checked)
-		painter_draw_rectangle(cb->x + 5, cb->y + 5, CHECKBOX_W - 9, CHECKBOX_H - 9);
-
-	if (cb->label) {
-		const unsigned int w = cb->w - (t->padding * 2) - CHECKBOX_W;
-		const int x = cb->x + (t->padding * 2) + CHECKBOX_W;
-
-		struct label label = {
-			.text = cb->label,
-			.align = LABEL_ALIGN_LEFT,
-			.x = x,
-			.y = cb->y,
-			.w = w,
-			.h = cb->h
-		};
-
-		draw_label(t, &label);
-	}
-}
-
-/* Default theme. */
-static struct theme default_theme = {
-	.colors = {
-		[THEME_COLOR_NORMAL]    = 0xffffffff,
-		[THEME_COLOR_SELECTED]  = 0x006554ff,
-		[THEME_COLOR_SHADOW]    = 0x000000ff
-	},
-	.padding = 10,
-	.draw_frame = draw_frame,
-	.draw_label = draw_label,
-	.draw_button = draw_button,
-	.draw_checkbox = draw_checkbox
-};
-
-/* Default font catalog. */
-#define FONT(bin, size, index)                                          \
-	{ bin, sizeof (bin), size, &default_theme.fonts[index], {0} }
-
-static struct font_catalog {
-	const unsigned char *data;
-	const size_t datasz;
-	unsigned int size;
-	struct font **dest;
-	struct font font;
-} default_fonts[] = {
-	FONT(fonts_f25_bank_printer, 10, THEME_FONT_DEBUG),
-	FONT(fonts_comic_neue, 20, THEME_FONT_INTERFACE)
-};
-
-bool
-theme_init(void)
-{
-	/* Open all fonts. */
-	for (size_t i = 0; i < NELEM(default_fonts); ++i) {
-		struct font_catalog *fc = &default_fonts[i];
-
-		if (!font_openmem(&fc->font, fc->data, fc->datasz, fc->size))
-			goto failed;
-
-		/* Reference this font into the catalog. */
-		*default_fonts[i].dest = &default_fonts[i].font;
-	}
-
-	return true;
-
-failed:
-	theme_finish();
-
-	return false;
-}
-
-struct theme *
-theme_default(void)
-{
-	return &default_theme;
-}
-
-unsigned int
-theme_padding(const struct theme *t)
-{
-	return THEME(t)->padding;
-}
-
-void
-theme_shallow(struct theme *dst, const struct theme *src)
-{
-	assert(dst);
-
-	memcpy(dst, src ? src : &default_theme, sizeof (*src));
-}
-
-void
-theme_draw_frame(struct theme *t, const struct frame *frame)
-{
-	assert(frame);
-
-	THEME(t)->draw_frame(THEME(t), frame);
-}
-
-void
-theme_draw_label(struct theme *t, const struct label *label)
-{
-	assert(label);
-
-	THEME(t)->draw_label(THEME(t), label);
-}
-
-void
-theme_draw_button(struct theme *t, const struct button *button)
-{
-	assert(button);
-
-	THEME(t)->draw_button(THEME(t), button);
-}
-
-void
-theme_draw_checkbox(struct theme *t, const struct checkbox *cb)
-{
-	assert(cb);
-
-	THEME(t)->draw_checkbox(THEME(t), cb);
-}
-
-void
-theme_finish(void)
-{
-	for (size_t i = 0; i < NELEM(default_fonts); ++i) {
-		font_finish(&default_fonts[i].font);
-		*default_fonts[i].dest = NULL;
-	}
-}
--- a/libcore/core/theme.h	Thu Oct 15 09:21:04 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,194 +0,0 @@
-/*
- * theme.h -- abstract theming
- *
- * Copyright (c) 2020 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef MOLKO_THEME_H
-#define MOLKO_THEME_H
-
-/**
- * \file theme.h
- * \brief Abstract theming.
- */
-
-#include <stdbool.h>
-
-struct checkbox;
-struct button;
-struct font;
-struct frame;
-struct label;
-
-/**
- * \brief Font component.
- */
-enum theme_font {
-	THEME_FONT_DEBUG,       /*!< Font for debug messages. */
-	THEME_FONT_INTERFACE,   /*!< Font for interface elements. */
-	THEME_FONT_LAST         /*!< Unused. */
-};
-
-/**
- * \brief Theme colors.
- */
-enum theme_color {
-	THEME_COLOR_DEBUG,      /*!< Debug color font. */
-	THEME_COLOR_NORMAL,     /*!< Normal font color. */
-	THEME_COLOR_SELECTED,   /*!< Font color for selected elements. */
-	THEME_COLOR_SHADOW,     /*!< Shadow color. */
-	THEME_COLOR_LAST        /*!< Unused. */
-};
-
-/**
- * \brief Abstract theme structure.
- */
-struct theme {
-	/**
-	 * (+&) Fonts catalog.
-	 */
-	struct font *fonts[THEME_FONT_LAST];
-
-	/**
-	 * (+) Miscellaneous colors.
-	 */
-	unsigned long colors[THEME_COLOR_LAST];
-
-	/**
-	 * (+) Padding between GUI elements.
-	 */
-	unsigned int padding;
-
-	/**
-	 * (+) Draw a frame.
-	 *
-	 * This function is used to draw a box usually as a container where UI
-	 * elements will be put.
-	 *
-	 * \see \ref theme_draw_frame
-	 */
-	void (*draw_frame)(struct theme *, const struct frame *);
-
-	/**
-	 * (+) Draw a label.
-	 *
-	 * \see \ref theme_draw_label
-	 */
-	void (*draw_label)(struct theme *, const struct label *);
-
-	/**
-	 * (+) Draw a button.
-	 *
-	 * \see \ref theme_draw_button
-	 */
-	void (*draw_button)(struct theme *, const struct button *);
-
-	/**
-	 * (+) Draw a checkbox.
-	 *
-	 * \see \ref theme_draw_button
-	 */
-	void (*draw_checkbox)(struct theme *t, const struct checkbox *);
-};
-
-/**
- * Initialize the theming system.
- *
- * \return false on errors
- * \warning This function must be called before any other theme functions.
- */
-bool
-theme_init(void);
-
-/**
- * Get a reference to the default theme.
- *
- * \return A non-owning pointer to a static storage for the default theme
- */
-struct theme *
-theme_default(void);
-
-/**
- * Convenient shortcut to shallow copy src into dst.
- *
- * Use this function when you want your own local copy of a theme because you
- * want to modify some attributes.
- *
- * This is a shortcut to `memcpy(dst, src, sizeof (*src))`.
- *
- * \pre dst != NULL
- * \param dst the destination theme
- * \param src the source theme (may be NULL)
- * \note Resources are not cloned, internal pointers will adress the same
- *       regions.
- */
-void
-theme_shallow(struct theme *dst, const struct theme *src);
-
-/**
- * Get the desired padding between GUI elements.
- *
- * \param t the theme to use (may be NULL)
- * \return the padding in pixels
- */
-unsigned int
-theme_padding(const struct theme *t);
-
-/**
- * Draw a frame.
- *
- * \pre frame != NULL
- * \param t the theme to use (may be NULL)
- * \param frame the frame
- */
-void
-theme_draw_frame(struct theme *t, const struct frame *frame);
-
-/**
- * Draw a label.
- *
- * \pre label != NULL
- * \param t the theme to use (may be NULL)
- * \param label the label
- */
-void
-theme_draw_label(struct theme *t, const struct label *label);
-
-/**
- * Draw a button.
- *
- * \pre button != NULL
- * \param t the theme to use (may be NULL)
- * \param button the button
- */
-void
-theme_draw_button(struct theme *t, const struct button *button);
-
-/**
- * Draw a checkbox.
- *
- * \param t the theme to use (may be NULL)
- * \param cb the checkbox
- */
-void
-theme_draw_checkbox(struct theme *t, const struct checkbox *cb);
-
-/**
- * Close associated resources.
- */
-void
-theme_finish(void);
-
-#endif /* !MOLKO_THEME_H */
--- a/libcore/core/walksprite.c	Thu Oct 15 09:21:04 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,60 +0,0 @@
-/*
- * walksprite.c -- sprite designed for walking entities
- *
- * Copyright (c) 2020 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <assert.h>
-#include <string.h>
-
-#include "walksprite.h"
-#include "sprite.h"
-
-void
-walksprite_init(struct walksprite *ws, struct sprite *sprite, unsigned int delay)
-{
-	assert(ws);
-	assert(sprite);
-
-	memset(ws, 0, sizeof (struct walksprite));
-	ws->sprite = sprite;
-	ws->delay = delay;
-}
-
-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(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/libcore/core/walksprite.h	Thu Oct 15 09:21:04 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,103 +0,0 @@
-/*
- * walksprite.h -- sprite designed for walking entities
- *
- * Copyright (c) 2020 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef MOLKO_WALKSPRITE_H
-#define MOLKO_WALKSPRITE_H
-
-/**
- * \file walksprite.h
- * \brief Sprite designed for walking entities.
- * \ingroup drawing
- */
-
-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;  /*!< (+&) The sprite to use */
-	unsigned int delay;     /*!< (+) The delay between frames */
-	unsigned int index;     /*!< (-) Current column index */
-	unsigned int elapsed;   /*!< (-) Elapsed time since last frame */
-};
-
-/**
- * Initialize the walking sprite.
- *
- * \pre ws != NULL
- * \pre sprite != NULL
- * \param ws the walking sprite
- * \param sprite the sprite to use
- * \param delay the delay between sprites
- */
-void
-walksprite_init(struct walksprite *ws, struct sprite *sprite, unsigned int delay);
-
-/**
- * Update the walking sprite
- *
- * \pre ws != NULL
- * \param ws the walking sprite
- * \param ticks the number of milliseconds between last frame
- */
-void
-walksprite_update(struct walksprite *ws, unsigned int ticks);
-
-/**
- * Draw the appropriate sprite cell to the screen according to the orientation
- * given.
- *
- * \pre ws != NULL
- * \pre orientation < 8
- * \param ws the walking sprite
- * \param orientation the orientation (or the row between 0 and 7)
- * \param x the x coordinate
- * \param y the y coordinate
- */
-void
-walksprite_draw(struct walksprite *ws, unsigned int orientation, int x, int y);
-
-#endif /* !MOLKO_WALKSPRITE_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/librpg/CMakeLists.txt	Thu Oct 15 10:32:18 2020 +0200
@@ -0,0 +1,49 @@
+#
+# CMakeLists.txt -- CMake build system for librpg
+#
+# Copyright (c) 2020 David Demelier <markand@malikania.fr>
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+#
+
+project(librpg)
+
+set(
+	SOURCES
+	${librpg_SOURCE_DIR}/rpg/inventory.c
+	${librpg_SOURCE_DIR}/rpg/inventory.h
+	${librpg_SOURCE_DIR}/rpg/inventory_dialog.c
+	${librpg_SOURCE_DIR}/rpg/inventory_dialog.h
+	${librpg_SOURCE_DIR}/rpg/item.h
+	${librpg_SOURCE_DIR}/rpg/map.c
+	${librpg_SOURCE_DIR}/rpg/map.h
+	${librpg_SOURCE_DIR}/rpg/map_state.c
+	${librpg_SOURCE_DIR}/rpg/map_state.h
+	${librpg_SOURCE_DIR}/rpg/message.c
+	${librpg_SOURCE_DIR}/rpg/message.h
+	${librpg_SOURCE_DIR}/rpg/walksprite.c
+	${librpg_SOURCE_DIR}/rpg/walksprite.h
+)
+
+molko_define_library(
+	TARGET librpg
+	SOURCES ${SOURCES}
+	LIBRARIES
+		libcore
+		libui
+	PUBLIC_INCLUDES
+		$<BUILD_INTERFACE:${librpg_SOURCE_DIR}>
+)
+
+source_group(rpg FILES ${SOURCES})
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/librpg/rpg/inventory.c	Thu Oct 15 10:32:18 2020 +0200
@@ -0,0 +1,210 @@
+/*
+ * inventory.c -- inventory of items
+ *
+ * Copyright (c) 2020 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <assert.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "inventory.h"
+#include "item.h"
+
+#define INVENTORY_TOTAL (INVENTORY_ROWS_MAX * INVENTORY_COLS_MAX)
+
+static bool
+can_be_used(struct inventory_slot *slot, const struct item *item)
+{
+	assert(item);
+
+	/* Empty slot. */
+	if (!slot->item)
+		return false;
+
+	/* Not same object. */
+	if (strcmp(slot->item->name, item->name) != 0)
+		return false;
+
+	/* No space in this slot. */
+	if (slot->amount >= slot->item->stackable)
+		return false;
+
+	return true;
+}
+
+static struct inventory_slot *
+find(struct inventory *iv, const struct item *item)
+{
+	assert(iv);
+	assert(item);
+
+	/* First pass: find an entry with the same item. */
+	for (unsigned int r = 0; r < INVENTORY_ROWS_MAX; ++r)
+		for (unsigned int c = 0; c < INVENTORY_COLS_MAX; ++c)
+			if (can_be_used(&iv->items[r][c], item))
+				return &iv->items[r][c];
+
+	/* Second pass: try to find an empty slot. */
+	for (unsigned int r = 0; r < INVENTORY_ROWS_MAX; ++r)
+		for (unsigned int c = 0; c < INVENTORY_COLS_MAX; ++c)
+			if (!iv->items[r][c].item)
+				return &iv->items[r][c];
+
+	return NULL;
+}
+
+static unsigned
+provide(struct inventory_slot *slot, struct item *item, unsigned int amount)
+{
+	assert(slot);
+
+	unsigned int avail;
+
+	/* The slot may be empty, make sure it contains this item. */
+	slot->item = item;
+
+	/*
+	 * Example:
+	 *
+	 * The slot has already 10 items.
+	 * The slot item is stackble up to 64 items.
+	 *
+	 * When pushing:
+	 *
+	 * 80: 54 pushed, 26 left
+	 * 30: 30 pushed, 0 left.
+	 */
+	avail = slot->item->stackable - slot->amount;
+
+	if (amount > avail) {
+		slot->amount += avail;
+		amount -= avail;
+	} else {
+		slot->amount += amount;
+		amount = 0;
+	}
+
+	return amount;
+}
+
+static bool
+merge(struct inventory_slot *slot, struct inventory_slot *other)
+{
+	assert(slot);
+	assert(slot->item);
+	assert(other);
+
+	/* Not compatible, return false to let the sorting continue. */
+	if (slot->item != other->item)
+		return false;
+
+	while (slot->amount < slot->item->stackable && other->amount) {
+		slot->amount++;
+		other->amount--;
+	}
+
+	/* No more amount in the other slot, empty it. */
+	if (other->amount == 0U)
+		memset(other, 0, sizeof (*other));
+
+	return slot->amount >= slot->item->stackable;
+}
+
+static void
+sort(struct inventory *iv, struct inventory_slot *slot, int r, int c)
+{
+	assert(slot);
+	assert(slot->item);
+
+	/* Merge until the end of thiw row. */
+	for (c = c + 1; c < INVENTORY_COLS_MAX; ++c)
+		if (merge(slot, &iv->items[r][c]))
+			return;
+
+	/* Merge the next rows. */
+	for (r = r + 1; r < INVENTORY_ROWS_MAX; ++r)
+		for (c = 0; c < INVENTORY_COLS_MAX; ++c)
+			if (merge(slot, &iv->items[r][c]))
+				return;
+}
+
+unsigned int
+inventory_push(struct inventory *iv, struct item *item, unsigned int amount)
+{
+	assert(iv);
+	assert(item);
+
+	while (amount) {
+		struct inventory_slot *slot;
+
+		if (!(slot = find(iv, item)))
+			break;
+
+		/* Add as much as we can in this slot. */
+		amount = provide(slot, item, amount);
+	}
+
+	return amount;
+}
+
+static int
+compare_slot(const void *v1, const void *v2)
+{
+	const struct inventory_slot *slot1 = v1;
+	const struct inventory_slot *slot2 = v2;
+	int cmp;
+
+	/* Two null slots compare equal. */
+	if (!slot1->item && !slot2->item)
+		return 0;
+
+	/* Null left should be moved after. */
+	if (!slot1->item)
+		return 1;
+
+	/* Null right slots should be moved after. */
+	if (!slot2->item)
+		return -1;
+
+	/* If they are identical, use amount to sort. */
+	if ((cmp = strcmp(slot1->item->name, slot2->item->name)) == 0)
+		return (long long int)slot2->amount - (long long int)slot1->amount;
+
+	return cmp;
+}
+
+void
+inventory_sort(struct inventory *iv)
+{
+	assert(iv);
+
+	for (int r = 0; r < INVENTORY_ROWS_MAX; ++r)
+		for (int c = 0; c < INVENTORY_COLS_MAX; ++c)
+			if (iv->items[r][c].item)
+				sort(iv, &iv->items[r][c], r, c);
+
+	/* Sort by names AND by amount. */
+	qsort(iv->items, INVENTORY_TOTAL, sizeof (struct inventory_slot), compare_slot);
+}
+
+void
+inventory_clear(struct inventory *iv)
+{
+	assert(iv);
+
+	memset(iv, 0, sizeof (*iv));
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/librpg/rpg/inventory.h	Thu Oct 15 10:32:18 2020 +0200
@@ -0,0 +1,92 @@
+/*
+ * inventory.h -- inventory of items
+ *
+ * Copyright (c) 2020 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef MOLKO_INVENTORY_H
+#define MOLKO_INVENTORY_H
+
+/**
+ * \file inventory.h
+ * \brief Inventory of items.
+ */
+
+/**
+ * \brief Maximum number of rows.
+ */
+#define INVENTORY_ROWS_MAX      3
+
+/**
+ * \brief Maximum number of columns.
+ */
+#define INVENTORY_COLS_MAX      10
+
+struct item;
+
+/**
+ * \brief Inventory slot.
+ *
+ * This structure describe a 'cell' into the inventory. It references a item
+ * and has a given amount of it.
+ */
+struct inventory_slot {
+	struct item *item;      /*!< (+&?) Pointer to the item. */
+	unsigned int amount;    /*!< (-) Number of items in this slot. */
+};
+
+/**
+ * \brief Inventory structure.
+ */
+struct inventory {
+	/**
+	 * (-) Grid of objects.
+	 */
+	struct inventory_slot items[INVENTORY_ROWS_MAX][INVENTORY_COLS_MAX];
+};
+
+/**
+ * Try to push as much as possible the given item.
+ *
+ * \pre iv != NULL
+ * \pre item != NULL
+ * \param iv the inventory
+ * \param item the item to reference
+ * \param amount the desired amount
+ * \return 0 if all items were pushed or the number left otherwise
+ */
+unsigned int
+inventory_push(struct inventory *iv, struct item *item, unsigned int amount);
+
+/**
+ * Sort the inventory.
+ *
+ * \pre iv != NULL
+ * \pre item != NULL
+ * \param iv the inventory
+ */
+void
+inventory_sort(struct inventory *iv);
+
+/**
+ * Clears the inventory.
+ *
+ * \pre iv != NULL
+ * \param iv the inventory
+ */
+void
+inventory_clear(struct inventory *iv);
+
+#endif /* !MOLKO_INVENTORY_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/librpg/rpg/inventory_dialog.c	Thu Oct 15 10:32:18 2020 +0200
@@ -0,0 +1,323 @@
+/*
+ * inventory_dialog.h -- dialog for items
+ *
+ * Copyright (c) 2020 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <assert.h>
+#include <stdio.h>
+
+#include <core/event.h>
+#include <core/game.h>
+#include <core/maths.h>
+#include <core/painter.h>
+#include <core/texture.h>
+#include <core/window.h>
+
+#include "inventory.h"
+#include "inventory_dialog.h"
+#include "item.h"
+
+#define ITEM_SIZE       64
+#define ITEM_PADDING    32
+
+#define GRID_WIDTH      ((INVENTORY_COLS_MAX * ITEM_SIZE) +             \
+                        ((INVENTORY_COLS_MAX + 1) * ITEM_PADDING))
+#define GRID_HEIGHT     ((INVENTORY_ROWS_MAX * ITEM_SIZE) +             \
+                        ((INVENTORY_ROWS_MAX + 1) * ITEM_PADDING))
+
+#define LABEL_WIDTH     (GRID_WIDTH)
+#define LABEL_HEIGHT    (64)
+
+#define BUTTON_HEIGHT   (32)
+#define BUTTON_WIDTH    ((GRID_WIDTH) / 4)
+
+/*
+ * The frame looks like this:
+ *
+ * +----------------------------+
+ * | [] [] [] [] [] [] [] [] [] |
+ * | [] [] [] [] [] [] [] [] [] |
+ * | [] [] [] [] [] [] [] [] [] |
+ * +----------------------------+
+ * | Item name                  |
+ * | Item description           |
+ * +----------------------------+
+ *                       [ Sort ]
+ *
+ * Where space between cells is determined with ITEM_PADDING macro.
+ */
+
+static unsigned int
+total_width(void)
+{
+	return GRID_WIDTH;
+}
+
+static unsigned int
+total_height(void)
+{
+	return GRID_HEIGHT + LABEL_HEIGHT + BUTTON_HEIGHT;
+}
+
+static void
+compute_box_position(const struct inventory_dialog *dlg, int r, int c, int *x, int *y)
+{
+	assert(dlg);
+	assert(x);
+	assert(y);
+
+	*x = dlg->fgrid.x + ((c * ITEM_SIZE) + ((c + 1) * ITEM_PADDING));
+	*y = dlg->fgrid.y + ((r * ITEM_SIZE) + ((r + 1) * ITEM_PADDING));
+}
+
+static void
+draw_grid_item_frame(int x, int y)
+{
+	struct frame frame = {
+		.x = x,
+		.y = y,
+		.w = ITEM_SIZE,
+		.h = ITEM_SIZE,
+		.style = FRAME_STYLE_BOX
+	};
+
+	frame_draw(&frame);
+}
+
+static void
+draw_grid_item_icon(struct item *item, int x, int y)
+{
+	struct texture *icon = item->icon;
+
+	texture_scale(icon, 0, 0, icon->w, icon->h, x, y, ITEM_SIZE, ITEM_SIZE, 0.0);
+}
+
+static void
+draw_grid_item_amount(struct inventory_slot *slot, int x, int y)
+{
+	char nstr[16];
+	struct label label = {
+		.text = nstr,
+		.x = x,
+		.y = y
+	};
+
+	/* Draw the number of item in this slot. */
+	snprintf(nstr, sizeof (nstr), "%d", slot->amount);
+	label_draw(&label);
+}
+
+static void
+draw_grid_item(struct inventory_slot *slot, int x, int y, bool selected)
+{
+	draw_grid_item_frame(x, y);
+
+	if (slot->item) {
+		draw_grid_item_icon(slot->item, x, y);
+		draw_grid_item_amount(slot, x, y);
+	}
+
+	if (selected) {
+		x -= 16;
+		y += (ITEM_SIZE / 2) - 4;
+		painter_draw_circle(x, y, 8);
+	}
+}
+
+static void
+draw_grid_items(const struct inventory_dialog *dlg)
+{
+	int x, y;
+	bool selected;
+
+	for (unsigned int r = 0; r < INVENTORY_ROWS_MAX; ++r) {
+		for (unsigned int c = 0; c < INVENTORY_COLS_MAX; ++c) {
+			selected = r == dlg->selrow && c == dlg->selcol;
+			compute_box_position(dlg, r, c, &x, &y);
+			draw_grid_item(&dlg->inv->items[r][c], x, y, selected);
+		}
+	}
+}
+
+static void
+draw_label(struct inventory_dialog *dlg)
+{
+	assert(dlg);
+
+	struct item *item = dlg->inv->items[dlg->selrow][dlg->selcol].item;
+
+	frame_draw(&dlg->fname);
+	frame_draw(&dlg->fdesc);
+
+	if (item) {
+		dlg->lname.text = item->name;
+		dlg->ldesc.text = item->summary;
+		label_draw(&dlg->lname);
+		label_draw(&dlg->ldesc);
+	}
+}
+
+void
+inventory_dialog_open(struct inventory_dialog *dlg)
+{
+	assert(dlg);
+	assert(dlg->inv);
+
+	int tw, th;
+
+	dlg->state = INVENTORY_DIALOG_SHOWING;
+
+	tw = total_width();
+	th = total_height();
+
+	/* Grid frame position. */
+	dlg->fgrid.w = GRID_WIDTH;
+	dlg->fgrid.h = GRID_HEIGHT;
+	dlg->fgrid.x = dlg->x;
+	dlg->fgrid.y = dlg->y;
+
+	/* Name label. */
+	dlg->fname.w = dlg->lname.w = LABEL_WIDTH;
+	dlg->fname.h = dlg->lname.h = LABEL_HEIGHT / 2;
+	dlg->fname.x = dlg->lname.x = dlg->x;
+	dlg->fname.y = dlg->lname.y = dlg->y + GRID_HEIGHT;
+	dlg->lname.x += ITEM_PADDING;
+	dlg->lname.align = LABEL_ALIGN_LEFT;
+
+	/* Description label. */
+	dlg->fdesc.w = dlg->ldesc.w = LABEL_WIDTH;
+	dlg->fdesc.h = dlg->ldesc.h = LABEL_HEIGHT / 2;
+	dlg->fdesc.x = dlg->ldesc.x = dlg->y;
+	dlg->fdesc.y = dlg->ldesc.y = dlg->y + GRID_HEIGHT + (LABEL_HEIGHT / 2);
+	dlg->ldesc.x += ITEM_PADDING;
+	dlg->ldesc.align = LABEL_ALIGN_LEFT;
+
+	/* Button sort. */
+	dlg->bsort.x = dlg->x;
+	dlg->bsort.y = dlg->y + GRID_HEIGHT + LABEL_HEIGHT;
+	dlg->bsort.w = BUTTON_WIDTH;
+	dlg->bsort.h = BUTTON_HEIGHT;
+	dlg->bsort.text = "Sort";
+}
+
+static void
+handle_keydown(struct inventory_dialog *dlg, const struct event_key *ev)
+{
+	assert(ev && ev->type == EVENT_KEYDOWN);
+
+	switch (ev->key) {
+	case KEY_LEFT:
+		if (dlg->selcol == 0)
+			dlg->selcol = INVENTORY_COLS_MAX - 1;
+		else
+			dlg->selcol--;
+		break;
+	case KEY_RIGHT:
+		dlg->selcol = (dlg->selcol + 1) % INVENTORY_COLS_MAX;
+		break;
+	case KEY_UP:
+		if (dlg->selrow == 0)
+			dlg->selrow = INVENTORY_ROWS_MAX - 1;
+		else
+			dlg->selrow--;
+		break;
+	case KEY_DOWN:
+		dlg->selrow = (dlg->selrow + 1) % INVENTORY_ROWS_MAX;
+		break;
+	default:
+		break;
+	}
+}
+
+static void
+handle_clickdown(struct inventory_dialog *dlg, const struct event_click *ev)
+{
+	assert(dlg);
+	assert(ev && ev->type == EVENT_CLICKDOWN);
+
+	int x, y;
+
+	for (int r = 0; r < INVENTORY_ROWS_MAX; ++r) {
+		for (int c = 0; c < INVENTORY_COLS_MAX; ++c) {
+			compute_box_position(dlg, r, c, &x, &y);
+
+			if (maths_is_boxed(x, y, ITEM_SIZE, ITEM_SIZE, ev->x, ev->y)) {
+				dlg->selrow = r;
+				dlg->selcol = c;
+			}
+		}
+	}
+}
+
+void
+inventory_dialog_handle(struct inventory_dialog *dlg, const union event *event)
+{
+	assert(event);
+
+	switch (event->type) {
+	case EVENT_KEYDOWN:
+		handle_keydown(dlg, &event->key);
+		break;
+	case EVENT_CLICKDOWN:
+		handle_clickdown(dlg, &event->click);
+		break;
+	default:
+		break;
+	}
+
+	button_handle(&dlg->bsort, event);
+
+	if (dlg->bsort.state == BUTTON_STATE_ACTIVATED) {
+		inventory_sort(dlg->inv);
+		button_reset(&dlg->bsort);
+	}
+}
+
+void
+inventory_dialog_update(struct inventory_dialog *dlg, unsigned int ticks)
+{
+	assert(dlg);
+
+	(void)ticks;
+}
+
+void
+inventory_dialog_move(struct inventory_dialog *dlg, int x, int y)
+{
+	assert(dlg);
+
+	dlg->x = x;
+	dlg->y = y;
+}
+
+void
+inventory_dialog_draw(struct inventory_dialog *dlg)
+{
+	assert(dlg);
+
+	frame_draw(&dlg->fgrid);
+	draw_grid_items(dlg);
+	draw_label(dlg);
+	button_draw(&dlg->bsort);
+}
+
+void
+inventory_dialog_finish(struct inventory_dialog *dlg)
+{
+	assert(dlg);
+
+	dlg->state = INVENTORY_DIALOG_NONE;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/librpg/rpg/inventory_dialog.h	Thu Oct 15 10:32:18 2020 +0200
@@ -0,0 +1,76 @@
+/*
+ * inventory_dialog.h -- dialog for items
+ *
+ * Copyright (c) 2020 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef INVENTORY_DIALOG_H
+#define INVENTORY_DIALOG_H
+
+#include <ui/button.h>
+#include <ui/frame.h>
+#include <ui/label.h>
+
+union event;
+
+struct inventory;
+struct theme;
+
+/**
+ * \brief Inventory dialog state.
+ */
+enum inventory_dialog_state {
+	INVENTORY_DIALOG_NONE,
+	INVENTORY_DIALOG_SHOWING
+};
+
+/**
+ * \brief Inventory dialog.
+ */
+struct inventory_dialog {
+	int x;                                  /*!< (+) Position in x. */
+	int y;                                  /*!< (+) Position in y. */
+	struct inventory *inv;                  /*!< (+&) Inventory to use. */
+	struct theme *theme;                    /*!< (+&?) Theme to use. */
+	struct button bsort;                    /*!< (-) Button sort. */
+	struct frame fgrid;                     /*!< (-) Grid frame. */
+	struct frame fname;                     /*!< (-) Frame for name. */
+	struct frame fdesc;                     /*!< (-) Frame for description. */
+	struct label lname;                     /*!< (-) Label for name. */
+	struct label ldesc;                     /*!< (-) Label for description. */
+	enum inventory_dialog_state state;      /*!< (-) Current dialog state. */
+	unsigned int selrow;                    /*!< (-) Current selected row. */
+	unsigned int selcol;                    /*!< (-) Current selected column. */
+};
+
+void
+inventory_dialog_open(struct inventory_dialog *dlg);
+
+void
+inventory_dialog_handle(struct inventory_dialog *dlg, const union event *event);
+
+void
+inventory_dialog_update(struct inventory_dialog *dlg, unsigned int ticks);
+
+void
+inventory_dialog_move(struct inventory_dialog *dlg, int x, int y);
+
+void
+inventory_dialog_draw(struct inventory_dialog *dlg);
+
+void
+inventory_dialog_finish(struct inventory_dialog *dlg);
+
+#endif /* !INVENTORY_DIALOG_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/librpg/rpg/item.h	Thu Oct 15 10:32:18 2020 +0200
@@ -0,0 +1,57 @@
+/*
+ * item.h -- inventory items
+ *
+ * Copyright (c) 2020 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef MOLKO_ITEM_H
+#define MOLKO_ITEM_H
+
+/**
+ * \file item.h
+ * \brief Inventory items.
+ */
+
+#include <stdbool.h>
+
+/**
+ * \brief Maximum count of an item into a stack.
+ */
+#define ITEM_STACK_MAX  64
+
+struct character;
+struct texture;
+
+/**
+ * \brief Inventory items.
+ */
+struct item {
+	const char *name;               /*!< (+) Name of item. */
+	const char *summary;            /*!< (+) Summary description. */
+	struct texture *icon;           /*!< (+&) Icon to show. */
+	unsigned int stackable;         /*!< (+) Stack count allowed. */
+
+	/**
+	 * (+) Execute the action for this character.
+	 */
+	void (*exec)(const struct item *, struct character *);
+
+	/**
+	 * (+?) Tells if the item can be used in this context.
+	 */
+	bool (*allowed)(const struct item *, const struct character *);
+};
+
+#endif /* !MOLKO_ITEM_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/librpg/rpg/map.c	Thu Oct 15 10:32:18 2020 +0200
@@ -0,0 +1,237 @@
+/*
+ * map.c -- game map
+ *
+ * Copyright (c) 2020 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <core/error.h>
+#include <core/error_p.h>
+#include <core/image.h>
+#include <core/painter.h>
+#include <core/sprite.h>
+#include <core/sys.h>
+#include <core/texture.h>
+#include <core/window.h>
+
+#include "map.h"
+
+/* Create %<v>c string literal for scanf */
+#define MAX_F(v) MAX_F_(v)
+#define MAX_F_(v) "%" #v "c"
+
+static void
+parse_layer(struct map_data *data, const char *line, FILE *fp)
+{
+	char layer_name[32 + 1] = { 0 };
+	struct map_layer *layer;
+	size_t amount, current;
+
+	/* Determine layer. */
+	if (sscanf(line, "layer|%32s", layer_name) <= 0)
+		return;
+	if (strcmp(layer_name, "background") == 0)
+		layer = &data->layers[0];
+	else if (strcmp(layer_name, "foreground") == 0)
+		layer = &data->layers[1];
+	else
+		return;
+
+	/* Check if weight/height has been specified. */
+	if (data->w == 0 || data->h == 0)
+		return;
+
+	amount = data->w * data->h;
+	current = 0;
+
+	if (!(layer->tiles = calloc(amount, sizeof (unsigned short))))
+		return;
+
+	for (int tile; fscanf(fp, "%d", &tile) && current < amount; ++current)
+		layer->tiles[current] = tile;
+}
+
+static void
+parse(struct map_data *data, const char *line, FILE *fp)
+{
+	if (strncmp(line, "title", 5) == 0)
+		sscanf(line, "title|" MAX_F(MAP_TITLE_MAX), data->title);
+	else if (strncmp(line, "width", 5) == 0)
+		sscanf(line, "width|%u", &data->w);
+	else if (strncmp(line, "height", 6) == 0)
+		sscanf(line, "height|%u", &data->h);
+	else if (strncmp(line, "tilewidth", 9) == 0)
+		sscanf(line, "tilewidth|%hu", &data->tile_w);
+	else if (strncmp(line, "tileheight", 10) == 0)
+		sscanf(line, "tileheight|%hu", &data->tile_h);
+	else if (strncmp(line, "origin", 6) == 0)
+		sscanf(line, "origin|%d|%d", &data->origin_x, &data->origin_y);
+	else if (strncmp(line, "tileset", 7) == 0)
+		sscanf(line, "tileset|" MAX_F(MAP_TILESET_MAX), data->tileset);
+	else if (strncmp(line, "layer", 5) == 0)
+		parse_layer(data, line, fp);
+}
+
+static bool
+check(struct map_data *data)
+{
+	if (strlen(data->title) == 0)
+		return error_printf("data has no title");
+	if (data->w == 0 || data->h == 0)
+		return error_printf("data has null sizes");
+	if (data->tile_w == 0 || data->tile_h == 0)
+		return error_printf("data has null tile sizes");
+	if (!data->layers[0].tiles || !data->layers[1].tiles)
+		return error_printf("could not allocate data");
+
+	return true;
+}
+
+static void
+draw_layer(struct map *map, const struct map_layer *layer)
+{
+	assert(map);
+	assert(layer);
+
+	struct sprite sprite;
+	int x = 0, y = 0;
+
+	sprite_init(&sprite, map->tileset, map->data->tile_w, map->data->tile_h);
+
+	for (unsigned int r = 0; r < map->data->w; ++r) {
+		for (unsigned int c = 0; c < map->data->h; ++c) {
+			unsigned int si = r * map->data->w + c;
+			unsigned int sr = (layer->tiles[si] - 1) / sprite.ncols;
+			unsigned int sc = (layer->tiles[si] - 1) % sprite.nrows;
+
+			if (layer->tiles[si] != 0)
+				sprite_draw(&sprite, sr, sc, x, y);
+
+			x += map->data->tile_w;
+		}
+
+		x = 0;
+		y += map->data->tile_h;
+	}
+}
+
+bool
+map_data_open_fp(struct map_data *data, FILE *fp)
+{
+	assert(data);
+
+	char line[1024];
+
+	if (!fp)
+		return false;
+
+	memset(data, 0, sizeof (*data));
+
+	while (fgets(line, sizeof (line), fp)) {
+		/* Remove \n if any */
+		line[strcspn(line, "\n")] = '\0';
+		parse(data, line, fp);
+	}
+
+	fclose(fp);
+
+	if (!check(data)) {
+		map_data_finish(data);
+		return false;
+	}
+
+	/* Compute real size. */
+	data->real_w = data->w * data->tile_w;
+	data->real_h = data->h * data->tile_h;
+
+	return true;
+}
+
+bool
+map_data_open(struct map_data *data, const char *path)
+{
+	assert(data);
+	assert(path);
+
+	return map_data_open_fp(data, fopen(path, "r"));
+}
+
+bool
+map_data_openmem(struct map_data *data, const void *buf, size_t bufsz)
+{
+	assert(data);
+	assert(buf);
+
+	return map_data_open_fp(data, fmemopen((void *)buf, bufsz, "r"));
+}
+
+void
+map_data_finish(struct map_data *data)
+{
+	assert(data);
+
+	free(data->layers[0].tiles);
+	free(data->layers[1].tiles);
+
+	memset(data, 0, sizeof (*data));
+}
+
+bool
+map_init(struct map *map, struct map_data *data, struct texture *tileset)
+{
+	assert(map);
+	assert(data);
+	assert(tileset && texture_ok(tileset));
+
+	if (!(texture_new(&map->picture, data->real_w, data->real_h)))
+		return false;
+
+	map->data = data;
+	map->tileset = tileset;
+
+	map_repaint(map);
+
+	return true;
+}
+
+void
+map_draw(struct map *map, int srcx, int srcy)
+{
+	texture_scale(&map->picture, srcx, srcy, window.w, window.h,
+	    0, 0, window.w, window.h, 0.0);
+}
+
+void
+map_repaint(struct map *map)
+{
+	PAINTER_BEGIN(&map->picture);
+	draw_layer(map, &map->data->layers[0]);
+	draw_layer(map, &map->data->layers[1]);
+	PAINTER_END();
+}
+
+void
+map_finish(struct map *map)
+{
+	assert(map);
+
+	texture_finish(&map->picture);
+
+	memset(map, 0, sizeof (*map));
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/librpg/rpg/map.h	Thu Oct 15 10:32:18 2020 +0200
@@ -0,0 +1,159 @@
+/*
+ * map.h -- game map
+ *
+ * Copyright (c) 2020 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef MOLKO_MAP_H
+#define MOLKO_MAP_H
+
+/**
+ * \file map.h
+ * \brief Game map.
+ */
+
+#include <stdbool.h>
+#include <stdio.h>
+
+#include <core/texture.h>
+
+/**
+ * \brief Max title length for a map.
+ */
+#define MAP_TITLE_MAX   32
+
+/**
+ * \brief Max filename for tilesets.
+ */
+#define MAP_TILESET_MAX FILENAME_MAX
+
+/**
+ * \brief Map layer.
+ */
+struct map_layer {
+	unsigned short *tiles;          /*!< (+) Array of tiles, depending on the map size. */
+};
+
+/**
+ * \brief Map definition structure.
+ *
+ * This structure only defines the map characteristics. It does not have any
+ * logic and is left for game state.
+ */
+struct map_data {
+	char title[MAP_TITLE_MAX];      /*!< (+) The map title. */
+	char tileset[MAP_TILESET_MAX];  /*!< (+) Name of tileset to use. */
+	int origin_x;                   /*!< (+) Where the player starts in X. */
+	int origin_y;                   /*!< (+) Where the player starts in Y. */
+	unsigned int real_w;            /*!< (-) Real width in pixels. */
+	unsigned int real_h;            /*!< (-) Real height in pixels. */
+	unsigned int w;                 /*!< (-) Map width in cells. */
+	unsigned int h;                 /*!< (-) Map height in cells. */
+	unsigned short tile_w;          /*!< (-) Pixels per cell (width). */
+	unsigned short tile_h;          /*!< (-) Pixels per cell (height). */
+	struct map_layer layers[2];     /*!< (+) Layers (background, foreground). */
+};
+
+/**
+ * \brief High level map object.
+ *
+ * This structure reference a map and perform drawing operations.
+ */
+struct map {
+	struct map_data *data;          /*!< (+&) Map data. */
+	struct texture *tileset;        /*!< (+&) Tileset to use. */
+	struct texture picture;         /*!< (-) Map drawn into a picture. */
+};
+
+/**
+ * Open a map defintion.
+ *
+ * \pre data != NULL
+ * \pre path != NULL
+ * \param data the map defintion to fill
+ * \param path the path to the map
+ * \return True if successfully loaded.
+ */
+bool
+map_data_open(struct map_data *data, const char *path);
+
+/**
+ * Open map data definition from memory.
+ *
+ *\pre data != NULL
+ *\pre buf != NULL
+ *\param data the map definition to fill
+ *\param buf the source buffer
+ *\param bufsz the source buffer size
+ */
+bool
+map_data_openmem(struct map_data *data, const void *buf, size_t bufsz);
+
+/**
+ * Dispose the map definition data.
+ *
+ * \pre data != NULL
+ * \param data the map definition
+ */
+void
+map_data_finish(struct map_data *data);
+
+/**
+ * Initialize this map.
+ *
+ * \pre map != NULL
+ * \pre data != NULL
+ * \pre tileset != NULL && texture_ok(tileset)
+ * \param map the map to initialize
+ * \param data the definition to reference
+ * \param tileset the tileset to use
+ * \return False on errors.
+ */
+bool
+map_init(struct map *map,
+         struct map_data *data,
+         struct texture *tileset);
+
+/**
+ * Render a map.
+ *
+ * \pre map != NULL
+ * \param map the map to render
+ * \param srcx the x coordinate region
+ * \param srcy the y coordinate region
+ */
+void
+map_draw(struct map *map, int srcx, int srcy);
+
+/**
+ * Force map repaint on its texture.
+ *
+ * \pre map != NULL
+ * \param map the map to repaint
+ * \warning This function does not render anything on the screen.
+ */
+void
+map_repaint(struct map *map);
+
+/**
+ * Dispose map resources.
+ *
+ * \pre map != NULL
+ * \param map the map to close
+ */
+void
+map_finish(struct map *map);
+
+#endif /* !MOLKO_MAP_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/librpg/rpg/map_state.c	Thu Oct 15 10:32:18 2020 +0200
@@ -0,0 +1,352 @@
+/*
+ * map_state.c -- state when player is on a map
+ *
+ * Copyright (c) 2020 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <stdio.h>
+
+#include <core/event.h>
+#include <core/game.h>
+#include <core/painter.h>
+#include <core/state.h>
+#include <core/texture.h>
+#include <core/window.h>
+
+#include <ui/debug.h>
+
+#include "map.h"
+#include "map_state.h"
+#include "walksprite.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    80
+#define MARGIN_HEIGHT   80
+
+/*
+ * Convenient macros to access the state data.
+ */
+#define MAP()           (&map_state_data.map)
+#define PLAYER()        (&map_state_data.player)
+#define VIEW()          (&map_state_data.view)
+
+/*
+ * 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
+};
+
+/*
+ * Additional data that is not necessary to expose in map_state_data.
+ */
+static struct {
+	struct {
+		enum movement moving;
+		struct walksprite ws;
+	} player;
+
+	struct {
+		int x;
+		int y;
+		unsigned int w;
+		unsigned int h;
+	} margin;
+} cache;
+
+static void
+center(void)
+{
+	VIEW()->x = PLAYER()->x - (VIEW()->w / 2);
+	VIEW()->y = PLAYER()->y - (VIEW()->h / 2);
+
+	if (VIEW()->x < 0)
+		VIEW()->x = 0;
+	else if ((unsigned int)VIEW()->x > MAP()->data.real_w - VIEW()->w)
+		VIEW()->x = MAP()->data.real_w - VIEW()->w;
+
+	if (VIEW()->y < 0)
+		VIEW()->y = 0;
+	else if ((unsigned int)VIEW()->y > MAP()->data.real_h - VIEW()->h)
+		VIEW()->y = MAP()->data.real_h - VIEW()->h;
+}
+
+static void
+enter(void)
+{
+	/* Adjust map properties. */
+	struct map *m = &map_state_data.map.map;
+
+	map_repaint(m);
+	MAP()->data.real_w = m->picture.w;
+	MAP()->data.real_h = m->picture.h;
+
+	/* Adjust view. */
+	VIEW()->w = window.w;
+	VIEW()->h = window.h;
+
+	/* Adjust margin. */
+	cache.margin.w = VIEW()->w - (MARGIN_WIDTH * 2);
+	cache.margin.h = VIEW()->h - (MARGIN_HEIGHT * 2);
+
+	/* Center the view by default. */
+	center();
+
+	/* Final bits. */
+	walksprite_init(&cache.player.ws, &PLAYER()->sprite, 300);
+}
+
+static void
+leave(void)
+{
+}
+
+static void
+handle_keydown(const union event *event)
+{
+	switch (event->key.key) {
+	case KEY_UP:
+		cache.player.moving |= MOVING_UP;
+		break;
+	case KEY_RIGHT:
+		cache.player.moving |= MOVING_RIGHT;
+		break;
+	case KEY_DOWN:
+		cache.player.moving |= MOVING_DOWN;
+		break;
+	case KEY_LEFT:
+		cache.player.moving |= MOVING_LEFT;
+		break;
+	default:
+		break;
+	}
+
+	PLAYER()->angle = orientations[cache.player.moving];
+}
+
+static void
+handle_keyup(const union event *event)
+{
+	switch (event->key.key) {
+	case KEY_UP:
+		cache.player.moving &= ~(MOVING_UP);
+		break;
+	case KEY_RIGHT:
+		cache.player.moving &= ~(MOVING_RIGHT);
+		break;
+	case KEY_DOWN:
+		cache.player.moving &= ~(MOVING_DOWN);
+		break;
+	case KEY_LEFT:
+		cache.player.moving &= ~(MOVING_LEFT);
+		break;
+	default:
+		break;
+	}
+}
+
+static void
+move_right(unsigned int delta)
+{
+	PLAYER()->x += delta;
+
+	if (PLAYER()->x > (int)(cache.margin.x + cache.margin.w)) {
+		VIEW()->x = (PLAYER()->x - VIEW()->w) + MARGIN_WIDTH;
+
+		if (VIEW()->x >= (int)(MAP()->data.real_w - VIEW()->w))
+			VIEW()->x = MAP()->data.real_w - VIEW()->w;
+	}
+
+	if (PLAYER()->x > (int)MAP()->data.real_w - 48)
+		PLAYER()->x = MAP()->data.real_w - 48;
+}
+
+static void
+move_left(unsigned int delta)
+{
+	PLAYER()->x -= delta;
+
+	if (PLAYER()->x < cache.margin.x) {
+		VIEW()->x = PLAYER()->x - MARGIN_WIDTH;
+
+		if (VIEW()->x < 0)
+			VIEW()->x = 0;
+	}
+
+	if (PLAYER()->x < 0)
+		PLAYER()->x = 0;
+}
+
+static void
+move_down(unsigned int delta)
+{
+	PLAYER()->y += delta;
+
+	if (PLAYER()->y > (int)(cache.margin.y + cache.margin.h)) {
+		VIEW()->y = (PLAYER()->y - VIEW()->h) + MARGIN_HEIGHT;
+
+		if (VIEW()->y >= (int)(MAP()->data.real_h - VIEW()->h))
+			VIEW()->y = MAP()->data.real_h - VIEW()->h;
+	}
+
+	if (PLAYER()->y > (int)MAP()->data.real_h - 48)
+		PLAYER()->y = MAP()->data.real_h - 48;
+}
+
+static void
+move_up(unsigned int delta)
+{
+	PLAYER()->y -= delta;
+
+	if (PLAYER()->y < cache.margin.y) {
+		VIEW()->y = PLAYER()->y - MARGIN_HEIGHT;
+
+		if (VIEW()->y < 0)
+			VIEW()->y = 0;
+	}
+
+	if (PLAYER()->y < 0)
+		PLAYER()->y = 0;
+}
+
+static void
+move(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. */
+	cache.margin.x = VIEW()->x + MARGIN_WIDTH;
+	cache.margin.y = VIEW()->y + MARGIN_HEIGHT;
+
+	int dx = 0;
+	int dy = 0;
+
+	if (cache.player.moving == 0)
+		return;
+
+	if (cache.player.moving & MOVING_UP)
+		dy = -1;
+	if (cache.player.moving & MOVING_DOWN)
+		dy = 1;
+	if (cache.player.moving & MOVING_LEFT)
+		dx = -1;
+	if (cache.player.moving & MOVING_RIGHT)
+		dx = 1;
+
+	/* Move the player and adjust view if needed. */
+	if (dx > 0)
+		move_right(delta);
+	else if (dx < 0)
+		move_left(delta);
+
+	if (dy > 0)
+		move_down(delta);
+	else if (dy < 0)
+		move_up(delta);
+
+	walksprite_update(&cache.player.ws, ticks);
+}
+
+static void
+handle(const union event *event)
+{
+	switch (event->type) {
+	case EVENT_KEYDOWN:
+		handle_keydown(event);
+		break;
+	case EVENT_KEYUP:
+		handle_keyup(event);
+		break;
+	default:
+		break;
+	}
+}
+
+static void
+update(unsigned int ticks)
+{
+	move(ticks);
+}
+
+static void
+draw(void)
+{
+	struct debug_report report = {0};
+
+	map_draw(&map_state_data.map.map, VIEW()->x, VIEW()->y);
+	walksprite_draw(
+		&cache.player.ws,
+		PLAYER()->angle,
+		PLAYER()->x - VIEW()->x,
+		PLAYER()->y - VIEW()->y);
+
+	debug_printf(&report, "position: %d, %d", PLAYER()->x,
+	    PLAYER()->y);
+	debug_printf(&report, "view: %d, %d", VIEW()->x,
+	    VIEW()->y);
+}
+
+struct map_state_data map_state_data;
+
+struct state map_state = {
+	.enter = enter,
+	.leave = leave,
+	.update = update,
+	.handle = handle,
+	.draw = draw
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/librpg/rpg/map_state.h	Thu Oct 15 10:32:18 2020 +0200
@@ -0,0 +1,75 @@
+/*
+ * map_state.h -- state when player is on a map
+ *
+ * Copyright (c) 2020 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef MOLKO_MAP_STATE_H
+#define MOLKO_MAP_STATE_H
+
+/**
+ * \file map_state.h
+ * \brief State when player is on a map.
+ * \ingroup states
+ */
+
+#include <core/sprite.h>
+
+#include "map.h"
+
+/**
+ * \brief Data for the state.
+ *
+ * Update this structure before switching to this state.
+ */
+extern struct map_state_data {
+	/**
+	 * Map properties.
+	 */
+	struct {
+		struct map_data data;   /*!< (+) Map data. */
+		struct map map;         /*!< (+) Map object. */
+	} map;
+
+	/**
+	 * Player position.
+	 *
+	 * If you adjust this structure, it is strictly encouraged to update
+	 * the view as well.
+	 */
+	struct {
+		struct sprite sprite;   /*!< (+) The sprite to use */
+		int x;                  /*!< (+) Player position in x */
+		int y;                  /*!< (+) Player position in y */
+		int angle;              /*!< (+) Player angle (see walksprite) */
+	} player;
+
+	/**
+	 * Position and size of the view.
+	 */
+	struct {
+		int x;                  /*!< (+) Position in x */
+		int y;                  /*!< (+) Position in y */
+		unsigned int w;         /*!< (+) View width */
+		unsigned int h;         /*!< (+) View height */
+	} view;
+} map_state_data; /*!< Access to data. */
+
+/**
+ * \brief State when player is on a map.
+ */
+extern struct state map_state;
+
+#endif /* !MOLKO_MAP_STATE_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/librpg/rpg/message.c	Thu Oct 15 10:32:18 2020 +0200
@@ -0,0 +1,271 @@
+/*
+ * message.c -- message dialog
+ *
+ * Copyright (c) 2020 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <core/action.h>
+#include <core/event.h>
+#include <core/font.h>
+#include <core/maths.h>
+#include <core/painter.h>
+#include <core/panic.h>
+#include <core/sprite.h>
+#include <core/trace.h>
+#include <core/util.h>
+
+#include <ui/frame.h>
+#include <ui/label.h>
+#include <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 bool
+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 void
+draw_lines(const struct message *msg)
+{
+	struct theme theme;
+	unsigned int lineh;
+
+	/* Shallow copy theme to modify colors. */
+	theme_shallow(&theme, msg->theme);
+
+	/* Compute text size for list alignment. */
+	lineh = font_height(theme.fonts[THEME_FONT_INTERFACE]);
+
+	for (int i = 0; i < 6; ++i) {
+		if (!msg->text[i])
+			continue;
+
+		struct label label = {
+			.y = i * lineh,
+			.w = msg->w,
+			.h = msg->h,
+			.theme = &theme,
+			.text = msg->text[i],
+			.align = LABEL_ALIGN_TOP_LEFT,
+			.flags = LABEL_FLAGS_SHADOW
+		};
+
+		/*
+		 * 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)
+			theme.colors[THEME_COLOR_NORMAL] = THEME(msg)->colors[THEME_COLOR_SELECTED];
+		else
+			theme.colors[THEME_COLOR_NORMAL] = THEME(msg)->colors[THEME_COLOR_NORMAL];
+
+		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)
+		trace("message is automatic but has zero timeout");
+}
+
+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 < 5 && msg->text[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;
+	}
+}
+
+bool
+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(struct message *msg)
+{
+	assert(msg);
+
+	struct texture tex;
+	int x, y;
+	unsigned int w, h;
+
+	if (!texture_new(&tex, msg->w, msg->h))
+		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. */
+	maths_centerize(&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/librpg/rpg/message.h	Thu Oct 15 10:32:18 2020 +0200
@@ -0,0 +1,197 @@
+/*
+ * message.h -- message dialog
+ *
+ * Copyright (c) 2020 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef MOLKO_MESSAGE_H
+#define MOLKO_MESSAGE_H
+
+/**
+ * \file message.h
+ * \brief Message dialog.
+ * \ingroup actions
+ * \ingroup drawing
+ *
+ * This module's purpose is to show a dialog box into the screen to show text
+ * and optionally ask the user a question.
+ *
+ * By itself, it is very low level and does not prevent other parts of the game
+ * to use the input so you probably need to inhibit input if your dialog is
+ * meant to be displayed on a map.
+ *
+ * To use it use the following procedure:
+ *
+ * 1. Create a struct message object and set required properties,
+ * 2. Call \ref message_start to reset the state,
+ * 3. Call \ref message_handle and \ref message_update with appropriate values,
+ * 4. Call \ref message_draw to render the dialog.
+ *
+ * Depending on message flags or user input, step 3 may return true in this
+ * case you should stop using the message as it has completed rendering.
+ *
+ * \note All properties must exist until the object is no longer used.
+ *
+ * \code
+ * struct message msg = {
+ * 	// You can show up to 6 lines.
+ * 	.text = {
+ * 		"Hello, what's up?"
+ * 	},
+ * 	// This image will be shown on the left as user face.
+ * 	.avatar = mysuperavatar,
+ * 	// This should point to a image that is used as background.
+ * 	.frame = mysuperframe,
+ * 	// The first color is normal text, the second is for selected text
+ * 	// in case of question.
+ * 	.colors = { 0xffffffff, 0x0000ffff },
+ * 	// This indicates this message is a question.
+ * 	.flags = MESSAGE_QUESTION
+ * };
+ * \endcode
+ */
+
+#include <stdbool.h>
+
+#include <core/texture.h>
+
+struct action;
+struct font;
+struct theme;
+
+union event;
+
+/**
+ * \brief Default animation speed in milliseconds.
+ */
+#define MESSAGE_DELAY_DEFAULT   (150)
+
+/**
+ * \brief Default timeout in milliseconds for automatic messages.
+ */
+#define MESSAGE_TIMEOUT_DEFAULT (5000)
+
+/**
+ * \brief Message flags.
+ */
+enum message_flags {
+	MESSAGE_FLAGS_AUTOMATIC         = (1 << 0),     /*!< Will automatically change state by itself. */
+	MESSAGE_FLAGS_QUESTION          = (1 << 1),     /*!< The message is a question. */
+	MESSAGE_FLAGS_FADEIN            = (1 << 2),     /*!< Animate opening. */
+	MESSAGE_FLAGS_FADEOUT           = (1 << 3)      /*!< Animate closing. */
+};
+
+/**
+ * \brief Message state.
+ */
+enum message_state {
+	MESSAGE_STATE_NONE,             /*!< Message hasn't start yet or is finished */
+	MESSAGE_STATE_OPENING,          /*!< Message animation is opening */
+	MESSAGE_STATE_SHOWING,          /*!< Message is displaying */
+	MESSAGE_STATE_HIDING            /*!< Message animation for hiding */
+};
+
+/**
+ * \brief Message object.
+ *
+ * This structure is used to display a message into the screen. It does not own
+ * any user properties and therefore must exist while using it.
+ */
+struct message {
+	int x;                          /*!< (+) Position in x. */
+	int y;                          /*!< (+) Position in y. */
+	unsigned int w;                 /*!< (+) Width. */
+	unsigned int h;                 /*!< (+) Height. */
+	unsigned int delay;             /*!< (+) Delay for animations. */
+	unsigned int timeout;           /*!< (+) Timeout in milliseconds. */
+	const char *text[6];            /*!< (+) Lines of text to show. */
+	struct texture *avatar;         /*!< (+&?) Avatar face. */
+	unsigned int index;             /*!< (+) Line selected */
+	enum message_flags flags;       /*!< (+) Message flags */
+	enum message_state state;       /*!< (-) Current state */
+	struct theme *theme;            /*!< (+&?) Theme to use. */
+	unsigned int elapsed;           /*!< (-) Time elapsed. */
+	double scale;                   /*!< (-) Current scale [0-1]. */
+};
+
+/**
+ * Start opening the message. This function will reset the message state and
+ * elapsed time.
+ *
+ * \pre msg != NULL
+ * \pre msg->delay > 0 if msg->flags contains MESSAGE_FLAGS_FADEIN or
+ *      MESSAGE_FLAGS_FADEOUT
+ * \param msg the message
+ */
+void
+message_start(struct message *msg);
+
+/**
+ * Handle input events.
+ *
+ * This function will alter state of the message and change its selection in
+ * case of question.
+ *
+ * \pre msg != NULL
+ * \pre ev != NULL
+ * \param msg the message
+ * \param ev the event which occured
+ */
+void
+message_handle(struct message *msg, const union event *ev);
+
+/**
+ * Update the message state and elapsed time..
+ *
+ * \pre msg != NULL
+ * \param msg the message
+ * \param ticks the elapsed delay since last frame
+ * \return true if it has finished
+ */
+bool
+message_update(struct message *msg, unsigned int ticks);
+
+/**
+ * Draw the message into the screen.
+ *
+ * \pre msg != NULL
+ * \param msg the message
+ */
+void
+message_draw(struct message *msg);
+
+/**
+ * Start hiding the message.
+ *
+ * \pre msg != NULL
+ * \param msg the message
+ * \note You should still continue to draw the message as the animation is not
+ *       finished!
+ */
+void
+message_hide(struct message *msg);
+
+/**
+ * Convert message into an action.
+ *
+ * \pre msg != NULL
+ * \pre action != NULL
+ * \param msg the message to reference
+ * \param action the action to fill
+ */
+void
+message_action(struct message *msg, struct action *action);
+
+#endif /* !MOLKO_MESSAGE_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/librpg/rpg/walksprite.c	Thu Oct 15 10:32:18 2020 +0200
@@ -0,0 +1,61 @@
+/*
+ * walksprite.c -- sprite designed for walking entities
+ *
+ * Copyright (c) 2020 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <assert.h>
+#include <string.h>
+
+#include <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 (struct walksprite));
+	ws->sprite = sprite;
+	ws->delay = delay;
+}
+
+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(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/librpg/rpg/walksprite.h	Thu Oct 15 10:32:18 2020 +0200
@@ -0,0 +1,103 @@
+/*
+ * walksprite.h -- sprite designed for walking entities
+ *
+ * Copyright (c) 2020 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef MOLKO_WALKSPRITE_H
+#define MOLKO_WALKSPRITE_H
+
+/**
+ * \file walksprite.h
+ * \brief Sprite designed for walking entities.
+ * \ingroup drawing
+ */
+
+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;  /*!< (+&) The sprite to use */
+	unsigned int delay;     /*!< (+) The delay between frames */
+	unsigned int index;     /*!< (-) Current column index */
+	unsigned int elapsed;   /*!< (-) Elapsed time since last frame */
+};
+
+/**
+ * Initialize the walking sprite.
+ *
+ * \pre ws != NULL
+ * \pre sprite != NULL
+ * \param ws the walking sprite
+ * \param sprite the sprite to use
+ * \param delay the delay between sprites
+ */
+void
+walksprite_init(struct walksprite *ws, struct sprite *sprite, unsigned int delay);
+
+/**
+ * Update the walking sprite
+ *
+ * \pre ws != NULL
+ * \param ws the walking sprite
+ * \param ticks the number of milliseconds between last frame
+ */
+void
+walksprite_update(struct walksprite *ws, unsigned int ticks);
+
+/**
+ * Draw the appropriate sprite cell to the screen according to the orientation
+ * given.
+ *
+ * \pre ws != NULL
+ * \pre orientation < 8
+ * \param ws the walking sprite
+ * \param orientation the orientation (or the row between 0 and 7)
+ * \param x the x coordinate
+ * \param y the y coordinate
+ */
+void
+walksprite_draw(struct walksprite *ws, unsigned int orientation, int x, int y);
+
+#endif /* !MOLKO_WALKSPRITE_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libui/CMakeLists.txt	Thu Oct 15 10:32:18 2020 +0200
@@ -0,0 +1,46 @@
+#
+# CMakeLists.txt -- CMake build system for libui
+#
+# Copyright (c) 2020 David Demelier <markand@malikania.fr>
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+#
+
+project(libui)
+
+set(
+	SOURCES
+	${libui_SOURCE_DIR}/ui/button.c
+	${libui_SOURCE_DIR}/ui/button.h
+	${libui_SOURCE_DIR}/ui/checkbox.c
+	${libui_SOURCE_DIR}/ui/checkbox.h
+	${libui_SOURCE_DIR}/ui/debug.c
+	${libui_SOURCE_DIR}/ui/debug.h
+	${libui_SOURCE_DIR}/ui/frame.c
+	${libui_SOURCE_DIR}/ui/frame.h
+	${libui_SOURCE_DIR}/ui/label.c
+	${libui_SOURCE_DIR}/ui/label.h
+	${libui_SOURCE_DIR}/ui/theme.c
+	${libui_SOURCE_DIR}/ui/theme.h
+)
+
+molko_define_library(
+	TARGET libui
+	SOURCES ${SOURCES}
+	LIBRARIES
+		libcore
+	PUBLIC_INCLUDES
+		$<BUILD_INTERFACE:${libui_SOURCE_DIR}>
+)
+
+source_group(ui FILES ${SOURCES})
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libui/ui/button.c	Thu Oct 15 10:32:18 2020 +0200
@@ -0,0 +1,79 @@
+/*
+ * button.c -- GUI button
+ *
+ * Copyright (c) 2020 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <assert.h>
+
+#include <core/event.h>
+#include <core/maths.h>
+
+#include "button.h"
+#include "theme.h"
+
+static bool
+is_boxed(const struct button *button, const struct event_click *click)
+{
+	assert(button);
+	assert(click);
+	assert(click->type == EVENT_CLICKDOWN || click->type == EVENT_CLICKUP);
+
+	return maths_is_boxed(button->x, button->y, button->w, button->h,
+	    click->x, click->y);
+}
+
+void
+button_handle(struct button *button, const union event *ev)
+{
+	assert(button);
+	assert(ev);
+
+	switch (ev->type) {
+	case EVENT_CLICKDOWN:
+		if (is_boxed(button, &ev->click))
+			button->state = BUTTON_STATE_PRESSED;
+		break;
+	case EVENT_CLICKUP:
+		/*
+		 * If the button was pressed, indicate that the button is
+		 * finally activated. This let the user to move the cursor
+		 * outside the button to "forget" the press.
+		 */
+		if (!is_boxed(button, &ev->click))
+			button->state = BUTTON_STATE_NONE;
+		else if (button->state == BUTTON_STATE_PRESSED)
+			button->state = BUTTON_STATE_ACTIVATED;
+		break;
+	default:
+		break;
+	}
+}
+
+void
+button_reset(struct button *button)
+{
+	assert(button);
+
+	button->state = BUTTON_STATE_NONE;
+}
+
+void
+button_draw(struct button *button)
+{
+	assert(button);
+
+	theme_draw_button(button->theme, button);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libui/ui/button.h	Thu Oct 15 10:32:18 2020 +0200
@@ -0,0 +1,85 @@
+/*
+ * button.h -- GUI button
+ *
+ * Copyright (c) 2020 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef MOLKO_BUTTON_H
+#define MOLKO_BUTTON_H
+
+/**
+ * \file button.h
+ * \brief GUI button.
+ */
+
+union event;
+
+struct theme;
+
+/**
+ * \brief Button state.
+ */
+enum button_state {
+	BUTTON_STATE_NONE,              /*!< Button is inactive. */
+	BUTTON_STATE_PRESSED,           /*!< Button is currently pressed. */
+	BUTTON_STATE_ACTIVATED          /*!< Button is considered activated. */
+};
+
+/**
+ * \brief GUI button.
+ */
+struct button {
+	int x;                          /*!< (+) Position in x. */
+	int y;                          /*!< (+) Position in y. */
+	unsigned int w;                 /*!< (+) Width. */
+	unsigned int h;                 /*!< (+) Height. */
+	const char *text;               /*!< (+&) Text to draw. */
+	enum button_state state;        /*!< (+) Button state. */
+	struct theme *theme;            /*!< (+&?) Theme to use. */
+};
+
+/**
+ * Handle the event.
+ *
+ * You should always call this function even if the event is completely
+ * unrelated.
+ *
+ * \pre button != NULL
+ * \pre ev != NULL
+ * \param button the button
+ * \param ev the event
+ */
+void
+button_handle(struct button *button, const union event *ev);
+
+/**
+ * Use this function once the button has been considered activated.
+ *
+ * \pre button != NULL
+ * \param button the button
+ */
+void
+button_reset(struct button *button);
+
+/**
+ * Draw the button.
+ *
+ * \pre button != NULL
+ * \param button the button
+ */
+void
+button_draw(struct button *button);
+
+#endif /* !MOLKO_BUTTON_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libui/ui/checkbox.c	Thu Oct 15 10:32:18 2020 +0200
@@ -0,0 +1,56 @@
+/*
+ * checkbox.c -- GUI checkbox
+ *
+ * Copyright (c) 2020 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <assert.h>
+
+#include <core/event.h>
+#include <core/maths.h>
+
+#include "checkbox.h"
+#include "theme.h"
+
+static bool
+is_boxed(const struct checkbox *cb, const struct event_click *click)
+{
+	assert(cb);
+	assert(click && click->type == EVENT_CLICKDOWN);
+
+	return maths_is_boxed(cb->x, cb->y, cb->w, cb->h, click->x, click->y);
+}
+
+void
+checkbox_handle(struct checkbox *cb, const union event *ev)
+{
+	assert(cb);
+	assert(ev);
+
+	switch (ev->type) {
+	case EVENT_CLICKDOWN:
+		if (is_boxed(cb, &ev->click))
+			cb->checked = !cb->checked;
+		break;
+	default:
+		break;
+	}
+}
+
+void
+checkbox_draw(const struct checkbox *cb)
+{
+	theme_draw_checkbox(cb->theme, cb);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libui/ui/checkbox.h	Thu Oct 15 10:32:18 2020 +0200
@@ -0,0 +1,64 @@
+/*
+ * checkbox.h -- GUI checkbox
+ *
+ * Copyright (c) 2020 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef MOLKO_CHECKBOX_H
+#define MOLKO_CHECKBOX_H
+
+/**
+ * \file checkbox.h
+ * \brief GUI checkbox.
+ */
+
+#include <stdbool.h>
+
+union event;
+
+/**
+ * \brief GUI checkbox.
+ */
+struct checkbox {
+	int x;                  /*!< (+) Position in x. */
+	int y;                  /*!< (+) Position in y. */
+	unsigned int w;         /*!< (+) Width. */
+	unsigned int h;         /*!< (+) Height. */
+	const char *label;      /*!< (+&) Text to show. */
+	bool checked;           /*!< (+) Is activated? */
+	struct theme *theme;    /*!< (+&?) Theme to use. */
+};
+
+/**
+ * Draw the checkbox.
+ *
+ * \pre cb != NULL
+ * \pre ev != NULL
+ * \param cb the checkbox
+ * \param ev the event
+ */
+void
+checkbox_handle(struct checkbox *cb, const union event *ev);
+
+/**
+ * Draw the checkbox.
+ *
+ * \pre cb != NULL
+ * \param cb the checkbox
+ */
+void
+checkbox_draw(const struct checkbox *cb);
+
+#endif /* !MOLKO_CHECKBOX_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libui/ui/debug.c	Thu Oct 15 10:32:18 2020 +0200
@@ -0,0 +1,82 @@
+/*
+ * debug.c -- debugging interfaces
+ *
+ * Copyright (c) 2020 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <assert.h>
+#include <stdio.h>
+
+#include <core/texture.h>
+
+#include "debug.h"
+#include "theme.h"
+
+#define PADDING_X 5
+#define PADDING_Y 5
+
+struct debug_options debug_options = {
+#if !defined(NDEBUG)
+	.enable = true
+#endif
+};
+
+void
+debug_printf(struct debug_report *report, const char *fmt, ...)
+{
+	assert(report);
+	assert(fmt);
+
+	if (!debug_options.enable)
+		return;
+
+	va_list ap;
+
+	va_start(ap, fmt);
+	debug_vprintf(report, fmt, ap);
+	va_end(ap);
+}
+
+void
+debug_vprintf(struct debug_report *report, const char *fmt, va_list ap)
+{
+	assert(report);
+	assert(fmt);
+
+	if (!debug_options.enable)
+		return;
+
+	char line[DEBUG_LINE_MAX];
+	struct theme *theme;
+	struct font *font;
+	struct texture tex;
+	unsigned int gapy;
+
+	vsnprintf(line, sizeof (line), fmt, ap);
+
+	theme = report->theme ? report->theme : theme_default();
+	font = theme->fonts[THEME_FONT_DEBUG];
+	font->color = report->color;
+
+	if (!font_render(font, &tex, line))
+		return;
+
+	gapy = (PADDING_Y * (report->count)) +
+	    (font_height(font) * (report->count));
+	report->count++;
+
+	texture_draw(&tex, PADDING_X, gapy);
+	texture_finish(&tex);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libui/ui/debug.h	Thu Oct 15 10:32:18 2020 +0200
@@ -0,0 +1,131 @@
+/*
+ * debug.h -- debugging interfaces
+ *
+ * Copyright (c) 2020 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef MOLKO_DEBUG_H
+#define MOLKO_DEBUG_H
+
+/**
+ * \file debug.h
+ * \brief Debugging interfaces.
+ *
+ * This module provides functions to draw debugging information within the game
+ * window. It is mostly used for information only when state of the game is
+ * better inspected via direct window information rather than writing in the
+ * console.
+ *
+ * Predefined core states may print debugging information if
+ * debug_options.enabled variable is set to true. However, you need to specify
+ * the font to use in order to work.
+ *
+ * Each call to \ref debug_printf or \ref debug_vprintf automatically adjust
+ * next coordinate for rendering the text. As such, user may simply print
+ * several lines of text without having to deal with that manually.
+ *
+ * Example, activate global debugging.
+ *
+ * \code
+ * struct font *f = font_openf("foo.ttf", 10);
+ *
+ * if (!f)
+ * 	error_fatal();
+ *
+ * debug_options.default_font = f;
+ * debug_options.enabled = true;
+ * \endcode
+ *
+ * Example, print debugging information manually.
+ *
+ * \code
+ * struct debug_report report = {
+ * 	.color = 0x00ff00ff,
+ * 	.font = myfont          // Optional if debug_options.default_font is set.
+ * };
+ *
+ * debug_printf("current position: %d, %d\n", x, y);
+ * \endcode
+ */
+
+#include <stdbool.h>
+#include <stdarg.h>
+
+#include <core/font.h>
+
+/**
+ * Maximum content length per report.
+ */
+#define DEBUG_LINE_MAX 1024
+
+struct theme;
+
+/**
+ * \brief Debugging options.
+ *
+ * Fill this structure with appropriate values to change debugging behavior
+ * in core API.
+ */
+struct debug_options {
+	bool enable;                    /*!< (+) Enable core API debugging. */
+};
+
+/**
+ * \brief Debug context.
+ *
+ * Use this structure each time you need to print one or more messages.
+ */
+struct debug_report {
+	struct theme *theme;            /*!< (+&?) Theme to use. */
+	unsigned long color;            /*!< (+) Font foreground color to use. */
+	unsigned int count;             /*!< (-) Number of messages already printed. */
+};
+
+/**
+ * Global debugging options.
+ */
+extern struct debug_options debug_options;
+
+/**
+ * Convenient macro to initialize \ref debug_report with default values.
+ */
+#define DEBUG_INIT_DEFAULTS {                   \
+        .font = debug_options.default_font,     \
+        .color = debug_options.default_color,   \
+        .style = debug_options.default_style    \
+}
+
+/**
+ * Print debugging information into the screen.
+ *
+ * \pre report != NULL
+ * \pre fmt != NULL
+ * \param report the reporting context
+ * \param fmt the printf(3) format string
+ * \note The message length must not exceed DEBUG_LINE_MAX, otherwise its
+ *       result is truncated.
+ */
+void
+debug_printf(struct debug_report *report, const char *fmt, ...);
+
+/**
+ * Similar to \ref debug_printf but with a va_list object.
+ *
+ * \see \ref debug_printf
+ */
+void
+debug_vprintf(struct debug_report *report, const char *fmt, va_list ap);
+
+#endif /* !MOLKO_DEBUG_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libui/ui/frame.c	Thu Oct 15 10:32:18 2020 +0200
@@ -0,0 +1,30 @@
+/*
+ * frame.h -- GUI frame
+ *
+ * Copyright (c) 2020 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <assert.h>
+
+#include "frame.h"
+#include "theme.h"
+
+void
+frame_draw(const struct frame *frame)
+{
+	assert(frame);
+
+	theme_draw_frame(frame->theme, frame);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libui/ui/frame.h	Thu Oct 15 10:32:18 2020 +0200
@@ -0,0 +1,58 @@
+/*
+ * frame.h -- GUI frame
+ *
+ * Copyright (c) 2020 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef MOLKO_FRAME_H
+#define MOLKO_FRAME_H
+
+/**
+ * \file frame.h
+ * \brief GUI frame.
+ */
+
+struct theme;
+
+/**
+ * \brief Frame style.
+ */
+enum frame_style {
+	FRAME_STYLE_NORMAL,
+	FRAME_STYLE_BOX
+};
+
+/**
+ * \brief GUI frame.
+ */
+struct frame {
+	int x;                  /*!< (+) Position in x. */
+	int y;                  /*!< (+) Position in y. */
+	unsigned int w;         /*!< (+) Width. */
+	unsigned int h;         /*!< (+) Height. */
+	enum frame_style style; /*!< (+) Frame style. */
+	struct theme *theme;    /*!< (+&?) Theme to use. */
+};
+
+/**
+ * Draw the frame.
+ *
+ * \pre frame != NULL
+ * \param frame the frame to draw
+ */
+void
+frame_draw(const struct frame *frame);
+
+#endif /* !MOLKO_FRAME_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libui/ui/label.c	Thu Oct 15 10:32:18 2020 +0200
@@ -0,0 +1,36 @@
+/*
+ * label.c -- GUI label
+ *
+ * Copyright (c) 2020 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <assert.h>
+
+#include <core/trace.h>
+
+#include "label.h"
+#include "theme.h"
+
+void
+label_draw(const struct label *label)
+{
+	assert(label);
+	assert(label->text);
+
+	if (label->w == 0 || label->h == 0)
+		trace("label %p has null dimensions", label);
+
+	theme_draw_label(label->theme, label);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libui/ui/label.h	Thu Oct 15 10:32:18 2020 +0200
@@ -0,0 +1,87 @@
+/*
+ * label.h -- GUI label
+ *
+ * Copyright (c) 2020 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef MOLKO_LABEL_H
+#define MOLKO_LABEL_H
+
+/**
+ * \file label.h
+ * \brief GUI label.
+ */
+
+struct theme;
+
+/**
+ * \brief Label flags.
+ */
+enum label_flags {
+	LABEL_FLAGS_NONE,                       /*!< No flags. */
+	LABEL_FLAGS_SHADOW      = (1 << 0),     /*!< Enable shadow. */
+};
+
+/**
+ * \brief Label alignment in bounding box.
+ *
+ * The alignment is described as following:
+ *
+ * ```
+ * +---------------------+
+ * | 1        2        3 |
+ * |                     |
+ * | 8        0        4 |
+ * |                     |
+ * | 7        6        5 |
+ * +---------------------+
+ * ```
+ */
+enum label_align {
+	LABEL_ALIGN_CENTER,             /*!< Align to the center (default). */
+	LABEL_ALIGN_TOP_LEFT,           /*!< Top left. */
+	LABEL_ALIGN_TOP,                /*!< Top center (aligned horizontally). */
+	LABEL_ALIGN_TOP_RIGHT,          /*!< Top right. */
+	LABEL_ALIGN_RIGHT,              /*!< Right (aligned vertically). */
+	LABEL_ALIGN_BOTTOM_RIGHT,       /*!< Bottom right. */
+	LABEL_ALIGN_BOTTOM,             /*!< Bottom (aligned horizontally). */
+	LABEL_ALIGN_BOTTOM_LEFT,        /*!< Bottom left. */
+	LABEL_ALIGN_LEFT                /*!< Left (aligned vertically). */
+};
+
+/**
+ * \brief GUI label.
+ */
+struct label {
+	int x;                          /*!< (+) Position in x. */
+	int y;                          /*!< (+) Position in y. */
+	unsigned int w;                 /*!< (+) Width. */
+	unsigned int h;                 /*!< (+) Height. */
+	const char *text;               /*!< (+&) Text to show. */
+	enum label_flags flags;         /*!< (+) Optional flags. */
+	enum label_align align;         /*!< (+) How to positionate label. */
+	struct theme *theme;            /*!< (+&?) Theme to use. */
+};
+
+/**
+ * Draw the label.
+ *
+ * \pre label != NULL
+ * \param label the label to draw
+ */
+void
+label_draw(const struct label *label);
+
+#endif /* !MOLKO_LABEL_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libui/ui/theme.c	Thu Oct 15 10:32:18 2020 +0200
@@ -0,0 +1,309 @@
+/*
+ * theme.c -- abstract theming
+ *
+ * Copyright (c) 2020 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <assert.h>
+#include <stddef.h>
+#include <string.h>
+
+#include <core/font.h>
+#include <core/maths.h>
+#include <core/painter.h>
+#include <core/panic.h>
+#include <core/texture.h>
+#include <core/util.h>
+
+#include <core/assets/fonts/f25-bank-printer.h>
+#include <core/assets/fonts/comic-neue.h>
+
+#include "button.h"
+#include "checkbox.h"
+#include "frame.h"
+#include "label.h"
+#include "theme.h"
+
+#define THEME(t) (t ? t : &default_theme)
+
+#define CHECKBOX_W 16
+#define CHECKBOX_H 16
+#define CHECKBOX_RAD 6
+
+static void
+box(int x, int y, unsigned int w, unsigned int h)
+{
+	/* Some basic outlines. */
+	painter_set_color(0x4d3533ff);
+
+	painter_draw_line(x, y, x + w, y);
+	painter_draw_line(x, y + h, x + w, y + h);
+	painter_draw_line(x, y, x, y + h);
+	painter_draw_line(x + w, y, x + w, y + h);
+}
+
+static void
+draw_frame(struct theme *t, const struct frame *frame)
+{
+	(void)t;
+
+	if (frame->style == FRAME_STYLE_BOX)
+		painter_set_color(0x6e4c30ff);
+	else
+		painter_set_color(0xce9248ff);
+
+	painter_draw_rectangle(frame->x, frame->y, frame->w, frame->h);
+	box(frame->x, frame->y, frame->w, frame->h);
+}
+
+static void
+draw_label(struct theme *t, const struct label *label)
+{
+	struct font *font;
+	struct texture tex;
+	int x, y, bx, by;
+	unsigned int tw, th, bw, bh;
+
+	/* Compute real box size according to padding. */
+	bx = label->x + t->padding;
+	by = label->y + t->padding;
+	bw = label->w - (t->padding * 2);
+	bh = label->h - (t->padding * 2);
+
+	/* Make a shallow copy of the interface font. */
+	font = t->fonts[THEME_FONT_INTERFACE];
+
+	/* Compute text size. */
+	if (!font_box(font, label->text, &tw, &th))
+		panic();
+
+	/* Compute position according to alignment and box. */
+	switch (label->align) {
+	case LABEL_ALIGN_CENTER:
+		maths_centerize(&x, &y, tw, th, bx, by, bw, bh);
+		break;
+	case LABEL_ALIGN_TOP_LEFT:
+		x = bx;
+		y = by;
+		break;
+	case LABEL_ALIGN_TOP:
+		maths_centerize(&x, NULL, tw, th, bx, by, bw, bh);
+		y = by;
+		break;
+	case LABEL_ALIGN_TOP_RIGHT:
+		x = bx + bw - tw;
+		y = by;
+		break;
+	case LABEL_ALIGN_RIGHT:
+		maths_centerize(NULL, &y, tw, th, bx, by, bw, bh);
+		x = bx + bw - tw;
+		break;
+	case LABEL_ALIGN_BOTTOM_RIGHT:
+		x = bx + bw - tw;
+		y = by + bh - th;
+		break;
+	case LABEL_ALIGN_BOTTOM:
+		maths_centerize(&x, NULL, tw, th, bx, by, bw, bh);
+		y = by + bh - th;
+		break;
+	case LABEL_ALIGN_BOTTOM_LEFT:
+		x = bx;
+		y = by + bh - th;
+		break;
+	case LABEL_ALIGN_LEFT:
+		maths_centerize(NULL, &y, tw, th, bx, by, bw, bh);
+		x = bx;
+	default:
+		break;
+	}
+
+	/* Shadow text, only if enabled. */
+	if (label->flags & LABEL_FLAGS_SHADOW) {
+		font->color = t->colors[THEME_COLOR_SHADOW];
+
+		if (!font_render(font, &tex, label->text))
+			panic();
+
+		texture_draw(&tex, x + 1, y + 1);
+		texture_finish(&tex);
+	}
+
+	/* Normal text. */
+	font->color = t->colors[THEME_COLOR_NORMAL];
+
+	if (!font_render(font, &tex, label->text))
+		panic();
+
+	texture_draw(&tex, x, y);
+	texture_finish(&tex);
+}
+
+static void
+draw_button(struct theme *t, const struct button *button)
+{
+	(void)t;
+
+	struct label label = {
+		.text = button->text,
+		.x = button->x,
+		.y = button->y,
+		.w = button->w,
+		.h = button->h
+	};
+
+	painter_set_color(0xabcdefff);
+	painter_draw_rectangle(button->x, button->y, button->w, button->h);
+
+	label_draw(&label);
+
+	box(button->x, button->y, button->w, button->h);
+}
+
+static void
+draw_checkbox(struct theme *t, const struct checkbox *cb)
+{
+	box(cb->x, cb->y, CHECKBOX_W, CHECKBOX_H);
+
+	if (cb->checked)
+		painter_draw_rectangle(cb->x + 5, cb->y + 5, CHECKBOX_W - 9, CHECKBOX_H - 9);
+
+	if (cb->label) {
+		const unsigned int w = cb->w - (t->padding * 2) - CHECKBOX_W;
+		const int x = cb->x + (t->padding * 2) + CHECKBOX_W;
+
+		struct label label = {
+			.text = cb->label,
+			.align = LABEL_ALIGN_LEFT,
+			.x = x,
+			.y = cb->y,
+			.w = w,
+			.h = cb->h
+		};
+
+		draw_label(t, &label);
+	}
+}
+
+/* Default theme. */
+static struct theme default_theme = {
+	.colors = {
+		[THEME_COLOR_NORMAL]    = 0xffffffff,
+		[THEME_COLOR_SELECTED]  = 0x006554ff,
+		[THEME_COLOR_SHADOW]    = 0x000000ff
+	},
+	.padding = 10,
+	.draw_frame = draw_frame,
+	.draw_label = draw_label,
+	.draw_button = draw_button,
+	.draw_checkbox = draw_checkbox
+};
+
+/* Default font catalog. */
+#define FONT(bin, size, index)                                          \
+	{ bin, sizeof (bin), size, &default_theme.fonts[index], {0} }
+
+static struct font_catalog {
+	const unsigned char *data;
+	const size_t datasz;
+	unsigned int size;
+	struct font **dest;
+	struct font font;
+} default_fonts[] = {
+	FONT(fonts_f25_bank_printer, 10, THEME_FONT_DEBUG),
+	FONT(fonts_comic_neue, 20, THEME_FONT_INTERFACE)
+};
+
+bool
+theme_init(void)
+{
+	/* Open all fonts. */
+	for (size_t i = 0; i < NELEM(default_fonts); ++i) {
+		struct font_catalog *fc = &default_fonts[i];
+
+		if (!font_openmem(&fc->font, fc->data, fc->datasz, fc->size))
+			goto failed;
+
+		/* Reference this font into the catalog. */
+		*default_fonts[i].dest = &default_fonts[i].font;
+	}
+
+	return true;
+
+failed:
+	theme_finish();
+
+	return false;
+}
+
+struct theme *
+theme_default(void)
+{
+	return &default_theme;
+}
+
+unsigned int
+theme_padding(const struct theme *t)
+{
+	return THEME(t)->padding;
+}
+
+void
+theme_shallow(struct theme *dst, const struct theme *src)
+{
+	assert(dst);
+
+	memcpy(dst, src ? src : &default_theme, sizeof (*src));
+}
+
+void
+theme_draw_frame(struct theme *t, const struct frame *frame)
+{
+	assert(frame);
+
+	THEME(t)->draw_frame(THEME(t), frame);
+}
+
+void
+theme_draw_label(struct theme *t, const struct label *label)
+{
+	assert(label);
+
+	THEME(t)->draw_label(THEME(t), label);
+}
+
+void
+theme_draw_button(struct theme *t, const struct button *button)
+{
+	assert(button);
+
+	THEME(t)->draw_button(THEME(t), button);
+}
+
+void
+theme_draw_checkbox(struct theme *t, const struct checkbox *cb)
+{
+	assert(cb);
+
+	THEME(t)->draw_checkbox(THEME(t), cb);
+}
+
+void
+theme_finish(void)
+{
+	for (size_t i = 0; i < NELEM(default_fonts); ++i) {
+		font_finish(&default_fonts[i].font);
+		*default_fonts[i].dest = NULL;
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libui/ui/theme.h	Thu Oct 15 10:32:18 2020 +0200
@@ -0,0 +1,194 @@
+/*
+ * theme.h -- abstract theming
+ *
+ * Copyright (c) 2020 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef MOLKO_THEME_H
+#define MOLKO_THEME_H
+
+/**
+ * \file theme.h
+ * \brief Abstract theming.
+ */
+
+#include <stdbool.h>
+
+struct checkbox;
+struct button;
+struct font;
+struct frame;
+struct label;
+
+/**
+ * \brief Font component.
+ */
+enum theme_font {
+	THEME_FONT_DEBUG,       /*!< Font for debug messages. */
+	THEME_FONT_INTERFACE,   /*!< Font for interface elements. */
+	THEME_FONT_LAST         /*!< Unused. */
+};
+
+/**
+ * \brief Theme colors.
+ */
+enum theme_color {
+	THEME_COLOR_DEBUG,      /*!< Debug color font. */
+	THEME_COLOR_NORMAL,     /*!< Normal font color. */
+	THEME_COLOR_SELECTED,   /*!< Font color for selected elements. */
+	THEME_COLOR_SHADOW,     /*!< Shadow color. */
+	THEME_COLOR_LAST        /*!< Unused. */
+};
+
+/**
+ * \brief Abstract theme structure.
+ */
+struct theme {
+	/**
+	 * (+&) Fonts catalog.
+	 */
+	struct font *fonts[THEME_FONT_LAST];
+
+	/**
+	 * (+) Miscellaneous colors.
+	 */
+	unsigned long colors[THEME_COLOR_LAST];
+
+	/**
+	 * (+) Padding between GUI elements.
+	 */
+	unsigned int padding;
+
+	/**
+	 * (+) Draw a frame.
+	 *
+	 * This function is used to draw a box usually as a container where UI
+	 * elements will be put.
+	 *
+	 * \see \ref theme_draw_frame
+	 */
+	void (*draw_frame)(struct theme *, const struct frame *);
+
+	/**
+	 * (+) Draw a label.
+	 *
+	 * \see \ref theme_draw_label
+	 */
+	void (*draw_label)(struct theme *, const struct label *);
+
+	/**
+	 * (+) Draw a button.
+	 *
+	 * \see \ref theme_draw_button
+	 */
+	void (*draw_button)(struct theme *, const struct button *);
+
+	/**
+	 * (+) Draw a checkbox.
+	 *
+	 * \see \ref theme_draw_button
+	 */
+	void (*draw_checkbox)(struct theme *t, const struct checkbox *);
+};
+
+/**
+ * Initialize the theming system.
+ *
+ * \return false on errors
+ * \warning This function must be called before any other theme functions.
+ */
+bool
+theme_init(void);
+
+/**
+ * Get a reference to the default theme.
+ *
+ * \return A non-owning pointer to a static storage for the default theme
+ */
+struct theme *
+theme_default(void);
+
+/**
+ * Convenient shortcut to shallow copy src into dst.
+ *
+ * Use this function when you want your own local copy of a theme because you
+ * want to modify some attributes.
+ *
+ * This is a shortcut to `memcpy(dst, src, sizeof (*src))`.
+ *
+ * \pre dst != NULL
+ * \param dst the destination theme
+ * \param src the source theme (may be NULL)
+ * \note Resources are not cloned, internal pointers will adress the same
+ *       regions.
+ */
+void
+theme_shallow(struct theme *dst, const struct theme *src);
+
+/**
+ * Get the desired padding between GUI elements.
+ *
+ * \param t the theme to use (may be NULL)
+ * \return the padding in pixels
+ */
+unsigned int
+theme_padding(const struct theme *t);
+
+/**
+ * Draw a frame.
+ *
+ * \pre frame != NULL
+ * \param t the theme to use (may be NULL)
+ * \param frame the frame
+ */
+void
+theme_draw_frame(struct theme *t, const struct frame *frame);
+
+/**
+ * Draw a label.
+ *
+ * \pre label != NULL
+ * \param t the theme to use (may be NULL)
+ * \param label the label
+ */
+void
+theme_draw_label(struct theme *t, const struct label *label);
+
+/**
+ * Draw a button.
+ *
+ * \pre button != NULL
+ * \param t the theme to use (may be NULL)
+ * \param button the button
+ */
+void
+theme_draw_button(struct theme *t, const struct button *button);
+
+/**
+ * Draw a checkbox.
+ *
+ * \param t the theme to use (may be NULL)
+ * \param cb the checkbox
+ */
+void
+theme_draw_checkbox(struct theme *t, const struct checkbox *cb);
+
+/**
+ * Close associated resources.
+ */
+void
+theme_finish(void);
+
+#endif /* !MOLKO_THEME_H */
--- a/molko/main.c	Thu Oct 15 09:21:04 2020 +0200
+++ b/molko/main.c	Thu Oct 15 10:32:18 2020 +0200
@@ -24,10 +24,11 @@
 #include <core/game.h>
 #include <core/panic.h>
 #include <core/sys.h>
-#include <core/theme.h>
 #include <core/util.h>
 #include <core/window.h>
 
+#include <ui/theme.h>
+
 #include <adventure/panic_state.h>
 #include <adventure/splashscreen_state.h>
 
--- a/tests/test-inventory.c	Thu Oct 15 09:21:04 2020 +0200
+++ b/tests/test-inventory.c	Thu Oct 15 10:32:18 2020 +0200
@@ -19,8 +19,8 @@
 #define GREATEST_USE_ABBREVS 0
 #include <greatest.h>
 
-#include <core/item.h>
-#include <core/inventory.h>
+#include <rpg/item.h>
+#include <rpg/inventory.h>
 
 static struct item potion = {
 	.name = "Potion",
--- a/tests/test-map.c	Thu Oct 15 09:21:04 2020 +0200
+++ b/tests/test-map.c	Thu Oct 15 10:32:18 2020 +0200
@@ -20,11 +20,12 @@
 #include <greatest.h>
 
 #include <core/error.h>
-#include <core/map.h>
 #include <core/panic.h>
 #include <core/sys.h>
 #include <core/window.h>
 
+#include <rpg/map.h>
+
 #include <assets/maps/sample-map.h>
 #include <assets/maps/error-title.h>
 #include <assets/maps/error-width.h>