changeset 322:eaab6bd635ab

misc: move examples from src/
author David Demelier <markand@malikania.fr>
date Sat, 02 Oct 2021 08:25:30 +0200
parents 687ac7516bbf
children fbc748292c01
files examples/example-action/main.c examples/example-animation/main.c examples/example-audio/main.c examples/example-battle/character-john.c examples/example-battle/character-john.h examples/example-battle/main.c examples/example-battle/registry.c examples/example-battle/registry.h examples/example-battle/spell-fire.c examples/example-battle/spell-fire.h examples/example-cursor/main.c examples/example-debug/main.c examples/example-drawable/main.c examples/example-font/main.c examples/example-gridmenu/main.c examples/example-label/main.c examples/example-message/main.c examples/example-sprite/main.c examples/example-trace/main.c examples/example-ui/main.c src/examples/example-action/main.c src/examples/example-animation/main.c src/examples/example-audio/main.c src/examples/example-battle/character-john.c src/examples/example-battle/character-john.h src/examples/example-battle/main.c src/examples/example-battle/registry.c src/examples/example-battle/registry.h src/examples/example-battle/spell-fire.c src/examples/example-battle/spell-fire.h src/examples/example-cursor/main.c src/examples/example-debug/main.c src/examples/example-drawable/main.c src/examples/example-font/main.c src/examples/example-gridmenu/main.c src/examples/example-label/main.c src/examples/example-message/main.c src/examples/example-sprite/main.c src/examples/example-trace/main.c src/examples/example-ui/main.c
diffstat 40 files changed, 3279 insertions(+), 3279 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/example-action/main.c	Sat Oct 02 08:25:30 2021 +0200
@@ -0,0 +1,399 @@
+/*
+ * example-action.c -- example on how to use automatic actions
+ *
+ * Copyright (c) 2020-2021 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <assert.h>
+
+#include <core/action.h>
+#include <core/core.h>
+#include <core/event.h>
+#include <core/game.h>
+#include <core/image.h>
+#include <core/maths.h>
+#include <core/painter.h>
+#include <core/panic.h>
+#include <core/script.h>
+#include <core/sprite.h>
+#include <core/state.h>
+#include <core/sys.h>
+#include <core/texture.h>
+#include <core/util.h>
+#include <core/window.h>
+
+#include <ui/theme.h>
+#include <ui/ui.h>
+
+#include <rpg/message.h>
+#include <rpg/rpg.h>
+
+#define W       1280
+#define H       720
+#define PATH(r) util_pathf("%s/mlk-adventure/%s", sys_dir(SYS_DIR_DATA), r)
+
+#define MW      (W * 0.75)
+#define MX      ((W / 2) - (MW / 2))
+#define MY      (100)
+
+/* This is a stack of "parallel" events. */
+static struct action_stack events;
+
+/* This is a stack of modal events. */
+static struct action_stack modal;
+
+/*
+ * Event object for the chest to click on.
+ */
+static struct {
+	struct message msg;
+	struct action msg_act;
+	int x;
+	int y;
+	int opened;
+	struct texture image;
+	struct sprite sprite;
+	struct action event;
+} chest = {
+	.msg = {
+		.x = MX,
+		.y = MY,
+		.w = MW,
+		.text = {
+			"100000 pièces.",
+			"Te voilà riche sale file de crevette."
+		}
+	}
+};
+
+/*
+ * Event object for the guide who ask dialog with you.
+ *
+ * It is a script spawned upon click like this.
+ *
+ * [guide.event]
+ *       |           spawn
+ *   (on click)------------------->[message 1]
+ *                                      v
+ *                                 [message 2]
+ *                                      v
+ *                                 [question 3]
+ *                   spawn              v
+ * [response]<---------------------[check result]
+ *     v
+ * [message 4/5]
+ */
+static struct {
+	struct {
+		struct message msg;
+		struct action act;
+	} msgs[5];
+
+	/* This is the sprite and action to click on. */
+	int x;
+	int y;
+	struct texture image;
+	struct sprite sprite;
+	struct action event;
+
+	/* This is the event for the response. */
+	struct action response;
+
+	struct script script;
+	struct action script_act;
+} guide = {
+	.msgs = {
+		{
+			.msg = {
+				.x = MX,
+				.y = MY,
+				.w = MW,
+				.delay = MESSAGE_DELAY_DEFAULT,
+				.flags = MESSAGE_FLAGS_FADEIN,
+				.text = {
+					"Bienvenue dans ce monde Molko."
+				}
+			},
+		},
+		{
+			.msg = {
+				.x = MX,
+				.y = MY,
+				.w = MW,
+				.text = {
+					"Penses tu vraiment pouvoir me battre ?"
+				}
+			}
+		},
+		{
+			.msg = {
+				.x = MX,
+				.y = MY,
+				.w = MW,
+				.flags = MESSAGE_FLAGS_QUESTION,
+				.text = {
+					"Non j'ai la trouille.",
+					"Bien sûr, je suis la légende."
+				}
+			}
+		},
+
+		/* In case of NO. */
+		{
+			.msg = {
+				.x = MX,
+				.y = MY,
+				.w = MW,
+				.text = {
+					"Poule mouillée."
+				}
+			}
+		},
+
+		/* In case of YES. */
+		{
+			.msg = {
+				.x = MX,
+				.y = MY,
+				.w = MW,
+				.text = {
+					"Prépare toi à souffrir."
+				}
+			}
+		}
+	}
+};
+
+static int
+guide_response_update(struct action *act, unsigned int ticks)
+{
+	/* Immediately return to get access to end. */
+	(void)act;
+	(void)ticks;
+
+	return 1;
+}
+
+static void
+guide_response_end(struct action *act)
+{
+	(void)act;
+
+	/* We add a final response depending on the result. */
+	const int index = guide.msgs[2].msg.index + 3;
+
+	message_action(&guide.msgs[index].msg, &guide.msgs[index].act);
+	message_query(&guide.msgs[index].msg, NULL, &guide.msgs[index].msg.h);
+	action_stack_add(&modal, &guide.msgs[index].act);
+}
+
+static void
+guide_popup(void)
+{
+	/* Prepare the script with first 3 messages. */
+	for (size_t i = 0; i < 3; ++i) {
+		message_action(&guide.msgs[i].msg, &guide.msgs[i].act);
+		message_query(&guide.msgs[i].msg, NULL, &guide.msgs[i].msg.h);
+		script_append(&guide.script, &guide.msgs[i].act);
+	}
+
+	/* Reset the message index. */
+	guide.msgs[2].msg.index = 0;
+
+	/* This is final action that analyze user input. */
+	guide.response.update = guide_response_update;
+	guide.response.end = guide_response_end;
+	script_append(&guide.script, &guide.response);
+
+	script_action(&guide.script, &guide.script_act);
+	action_stack_add(&modal, &guide.script_act);
+}
+
+static void
+guide_handle(struct action *act, const union event *ev)
+{
+	(void)act;
+
+	if (!action_stack_completed(&modal))
+		return;
+
+	switch (ev->type) {
+	case EVENT_CLICKDOWN:
+		if (maths_is_boxed(guide.x, guide.y,
+		    guide.sprite.cellw, guide.sprite.cellh,
+		    ev->click.x, ev->click.y))
+			guide_popup();
+		break;
+	default:
+		break;
+	}
+}
+
+static void
+guide_draw(struct action *act)
+{
+	(void)act;
+
+	sprite_draw(&guide.sprite, 0, 0, guide.x, guide.y);
+}
+
+static void
+guide_init(void)
+{
+	if (image_open(&guide.image, PATH("sprites/people.png")) < 0)
+		panic();
+
+	sprite_init(&guide.sprite, &guide.image, 48, 48);
+
+	/* This is the global guide action. */
+	guide.event.handle = guide_handle;
+	guide.event.draw = guide_draw;
+	guide.x = 200;
+	guide.y = 200;
+}
+
+static void
+chest_handle(struct action *act, const union event *ev)
+{
+	(void)act;
+
+	if (chest.opened || !action_stack_completed(&modal))
+		return;
+
+	switch (ev->type) {
+	case EVENT_CLICKDOWN:
+		if (maths_is_boxed(chest.x, chest.y, chest.sprite.cellw, chest.sprite.cellh,
+		    ev->click.x, ev->click.y)) {
+			chest.opened = 1;
+			message_action(&chest.msg, &chest.msg_act);
+			message_query(&chest.msg, NULL, &chest.msg.h);
+			action_stack_add(&modal, &chest.msg_act);
+		}
+	default:
+		break;
+	}
+}
+
+static void
+chest_draw(struct action *act)
+{
+	(void)act;
+
+	const int row = chest.opened ? chest.sprite.nrows - 1 : 0;
+	const int col = chest.opened ? chest.sprite.ncols - 1 : 0;
+
+	sprite_draw(&chest.sprite, row, col, chest.x, chest.y);
+}
+
+static void
+chest_init(void)
+{
+	if (image_open(&chest.image, PATH("sprites/chest.png")) < 0)
+		panic();
+
+	sprite_init(&chest.sprite, &chest.image, 32, 32);
+
+	chest.x = 100;
+	chest.y = 100;
+	chest.event.handle = chest_handle;
+	chest.event.draw = chest_draw;
+}
+
+static void
+init(void)
+{
+	if (core_init("fr.malikania", "actions") < 0 || ui_init() < 0 || rpg_init() < 0)
+		panic();
+	if (window_open("Example - Action", W, H) < 0)
+		panic();
+
+	guide_init();
+	chest_init();
+}
+
+static void
+handle(struct state *st, const union event *ev)
+{
+	(void)st;
+
+	switch (ev->type) {
+	case EVENT_QUIT:
+		game_quit();
+		break;
+	default:
+		action_stack_handle(&events, ev);
+		action_stack_handle(&modal, ev);
+		break;
+	}
+}
+
+static void
+update(struct state *st, unsigned int ticks)
+{
+	(void)st;
+
+	action_stack_update(&events, ticks);
+	action_stack_update(&modal, ticks);
+}
+
+static void
+draw(struct state *st)
+{
+	(void)st;
+
+	painter_set_color(0xffffffff);
+	painter_clear();
+	action_stack_draw(&events);
+	action_stack_draw(&modal);
+	painter_present();
+}
+
+static void
+run(void)
+{
+	struct state state = {
+		.handle = handle,
+		.update = update,
+		.draw = draw
+	};
+
+	action_stack_add(&events, &chest.event);
+	action_stack_add(&events, &guide.event);
+
+	game_push(&state);
+	game_loop();
+}
+
+static void
+quit(void)
+{
+	window_finish();
+	rpg_finish();
+	ui_finish();
+	core_finish();
+}
+
+int
+main(int argc, char **argv)
+{
+	(void)argc;
+	(void)argv;
+
+	init();
+	run();
+	quit();
+
+	return 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/example-animation/main.c	Sat Oct 02 08:25:30 2021 +0200
@@ -0,0 +1,145 @@
+/*
+ * example-animation.c -- example on how to use animations
+ *
+ * Copyright (c) 2020-2021 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <core/animation.h>
+#include <core/core.h>
+#include <core/event.h>
+#include <core/game.h>
+#include <core/image.h>
+#include <core/sys.h>
+#include <core/window.h>
+#include <core/painter.h>
+#include <core/panic.h>
+#include <core/sprite.h>
+#include <core/state.h>
+#include <core/texture.h>
+#include <core/util.h>
+
+#include <ui/label.h>
+#include <ui/ui.h>
+
+#define W       1280
+#define H       720
+#define PATH(r) util_pathf("%s/mlk-adventure/%s", sys_dir(SYS_DIR_DATA), r)
+
+static struct label label = {
+	.text = "Keys: <Space> start or reset the animation.",
+	.x = 10,
+	.y = 10,
+	.flags = LABEL_FLAGS_SHADOW
+};
+
+static struct texture numbers;
+static struct animation animation;
+static struct sprite sprite;
+static int completed = 1;
+
+static void
+init(void)
+{
+	if (core_init("fr.malikania", "animation") < 0 || ui_init() < 0)
+		panic();
+	if (window_open("Example - Animation", W, H) < 0)
+		panic();
+	if (image_open(&numbers, PATH("sprites/numbers.png")) < 0)
+		panic();
+}
+
+static void
+handle(struct state *st, const union event *ev)
+{
+	(void)st;
+
+	switch (ev->type) {
+	case EVENT_KEYDOWN:
+		switch (ev->key.key) {
+		case KEY_SPACE:
+			animation_start(&animation);
+			completed = animation_completed(&animation);
+			break;
+		default:
+			break;
+		}
+		break;
+	case EVENT_QUIT:
+		game_quit();
+		break;
+	default:
+		break;
+	}
+}
+
+static void
+update(struct state *st, unsigned int ticks)
+{
+	(void)st;
+
+	if (!completed)
+		completed = animation_update(&animation, ticks);
+}
+
+static void
+draw(struct state *st)
+{
+	(void)st;
+
+	painter_set_color(0x4f8fbaff);
+	painter_clear();
+	label_draw(&label);
+
+	if (!completed)
+		animation_draw(&animation, (window.w - sprite.cellw) / 2, (window.h - sprite.cellh) / 2);
+
+	painter_present();
+}
+
+static void
+run(void)
+{
+	struct state state = {
+		.handle = handle,
+		.update = update,
+		.draw = draw
+	};
+
+	sprite_init(&sprite, &numbers, 48, 48);
+	animation_init(&animation, &sprite, 1000);
+
+	game_push(&state);
+	game_loop();
+}
+
+static void
+quit(void)
+{
+	window_finish();
+	ui_finish();
+	core_finish();
+}
+
+int
+main(int argc, char **argv)
+{
+	(void)argc;
+	(void)argv;
+
+	init();
+	run();
+	quit();
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/example-audio/main.c	Sat Oct 02 08:25:30 2021 +0200
@@ -0,0 +1,157 @@
+/*
+ * example-audio.c -- show how to use sounds and music
+ *
+ * Copyright (c) 2020-2021 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <core/core.h>
+#include <core/event.h>
+#include <core/game.h>
+#include <core/music.h>
+#include <core/painter.h>
+#include <core/panic.h>
+#include <core/sound.h>
+#include <core/state.h>
+#include <core/sys.h>
+#include <core/util.h>
+#include <core/window.h>
+
+#include <ui/label.h>
+#include <ui/theme.h>
+#include <ui/ui.h>
+
+#define W       1280
+#define H       720
+#define PATH(r) util_pathf("%s/mlk-adventure/%s", sys_dir(SYS_DIR_DATA), r)
+
+static struct music music;
+static struct sound sound;
+
+static struct label label_music = {
+	.text = "Music: <Space> play, <f> fade in, <s> fade out, <p> pause, <r> resume, <q> stop, <l> loop.",
+	.x = 10,
+	.y = 10,
+	.flags = LABEL_FLAGS_SHADOW
+};
+
+static struct label label_sound = {
+	.text = "Sound: click anywhere to pop a sound.",
+	.x = 10,
+	.y = 30,
+	.flags = LABEL_FLAGS_SHADOW
+};
+
+static void
+init(void)
+{
+	if (core_init("fr.malikania", "audio") < 0 || ui_init() < 0)
+		panic();
+	if (window_open("Example - Audio", W, H) < 0)
+		panic();
+	if (music_open(&music, PATH("music/vabsounds-romance.ogg")) < 0 ||
+	    sound_open(&sound, PATH("sounds/fire.wav")) < 0)
+		panic();
+}
+
+static void
+quit(void)
+{
+	window_finish();
+	ui_finish();
+	core_finish();
+}
+
+static void
+handle(struct state *st, const union event *ev)
+{
+	(void)st;
+
+	switch (ev->type) {
+	case EVENT_CLICKDOWN:
+		if (sound_play(&sound, -1, 0) < 0)
+			panic();
+		break;
+	case EVENT_KEYDOWN:
+		switch (ev->key.key) {
+		case KEY_f:
+			music_play(&music, 0, 500);
+			break;
+		case KEY_s:
+			music_stop(500);
+			break;
+		case KEY_p:
+			music_pause();
+			break;
+		case KEY_r:
+			music_resume();
+			break;
+		case KEY_q:
+			music_stop(0);
+			break;
+		case KEY_l:
+			music_play(&music, MUSIC_LOOP, 0);
+			break;
+		case KEY_SPACE:
+			music_play(&music, 0, 0);
+			break;
+		default:
+			break;
+		}
+		break;
+	case EVENT_QUIT:
+		game_quit();
+		break;
+	default:
+		break;
+	}
+}
+
+static void
+draw(struct state *st)
+{
+	(void)st;
+
+	painter_set_color(0x006554ff);
+	painter_clear();
+	label_draw(&label_music);
+	label_draw(&label_sound);
+	painter_present();
+}
+
+static void
+run(void)
+{
+	struct state state = {
+		.handle = handle,
+		.draw = draw
+	};
+
+	game_push(&state);
+	game_loop();
+
+	music_finish(&music);
+	sound_finish(&sound);
+}
+
+int
+main(int argc, char **argv)
+{
+	(void)argc;
+	(void)argv;
+
+	init();
+	run();
+	quit();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/example-battle/character-john.c	Sat Oct 02 08:25:30 2021 +0200
@@ -0,0 +1,48 @@
+/*
+ * character-john.c -- john character
+ *
+ * Copyright (c) 2020-2021 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <rpg/character.h>
+
+#include "character-john.h"
+#include "spell-fire.h"
+#include "registry.h"
+
+static void
+adventurer_reset(struct character *ch)
+{
+	/* TODO: this function should compute the attack thanks to the level. */
+	ch->hpmax = 120;
+	ch->mpmax = 50;
+	ch->atk = 50;
+	ch->def = 50;
+	ch->agt = 50;
+	ch->luck = 50;
+}
+
+const struct character character_john = {
+	.name = "John ",
+	.type = "Adventurer",
+	.level = 1,
+	.hp = 120,
+	.mp = 50,
+	.reset = adventurer_reset,
+	.sprite = &registry_sprites[REGISTRY_TEXTURE_JOHN],
+	.spells = {
+		&spell_fire
+	}
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/example-battle/character-john.h	Sat Oct 02 08:25:30 2021 +0200
@@ -0,0 +1,24 @@
+/*
+ * character-john.h -- john character
+ *
+ * Copyright (c) 2020-2021 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef EXAMPLES_BATTLE_CHARACTER_JOHN_H
+#define EXAMPLES_BATTLE_CHARACTER_JOHN_H
+
+const struct character character_john;
+
+#endif /* !EXAMPLES_BATTLE_CHARACTER_JOHN_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/example-battle/main.c	Sat Oct 02 08:25:30 2021 +0200
@@ -0,0 +1,295 @@
+/*
+ * example-battle.c -- show how to use battle
+ *
+ * Copyright (c) 2020-2021 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <core/alloc.h>
+#include <core/core.h>
+#include <core/event.h>
+#include <core/game.h>
+#include <core/image.h>
+#include <core/painter.h>
+#include <core/panic.h>
+#include <core/sprite.h>
+#include <core/state.h>
+#include <core/sys.h>
+#include <core/texture.h>
+#include <core/util.h>
+#include <core/window.h>
+
+#include <ui/align.h>
+#include <ui/label.h>
+#include <ui/theme.h>
+#include <ui/ui.h>
+
+#include <rpg/character.h>
+#include <rpg/battle.h>
+#include <rpg/rpg.h>
+#include <rpg/spell.h>
+
+#include "registry.h"
+#include "spell-fire.h"
+
+#define W 1280
+#define H 720
+
+static void
+adventurer_reset(struct character *ch)
+{
+	/* TODO: this function should compute the attack thanks to the level. */
+	ch->hpmax = 120;
+	ch->mpmax = 50;
+	ch->atk = 50;
+	ch->def = 50;
+	ch->agt = 50;
+	ch->luck = 50;
+}
+
+static void
+haunted_wood_reset(struct character *ch)
+{
+	ch->hpmax = ch->hp = 2000;
+	ch->mpmax = ch->mp = 250;
+	ch->atk = 178;
+	ch->def = 80;
+	ch->agt = 80;
+	ch->luck = 100;
+}
+
+static void
+black_cat_reset(struct character *ch)
+{
+	ch->hpmax = ch->hp = 126;
+	ch->mpmax = ch->mp = 38;
+	ch->atk = 22;
+	ch->def = 19;
+	ch->agt = 11;
+	ch->luck = 14;
+}
+
+static struct character team[] = {
+	{
+		.name = "Molko",
+		.level = 1,
+		.hp = 120,
+		.mp = 50,
+		.reset = adventurer_reset,
+		.sprites = {
+			[CHARACTER_SPRITE_NORMAL] = &registry_sprites[REGISTRY_TEXTURE_JOHN_WALK],
+			[CHARACTER_SPRITE_SWORD] = &registry_sprites[REGISTRY_TEXTURE_JOHN_SWORD],
+		},
+		.spells = {
+			&spell_fire
+		}
+	},
+};
+
+static void
+haunted_wood_strat(struct character *ch, struct battle *bt)
+{
+	(void)ch;
+
+	/* TODO: Select randomly. */
+	battle_attack(bt, bt->order_cur->ch, bt->team[0].ch);
+}
+
+static void
+black_cat_strat(struct character *ch, struct battle *bt)
+{
+	(void)ch;
+
+	/* TODO: Select randomly. */
+	battle_attack(bt, bt->order_cur->ch, bt->team[0].ch);
+}
+
+static struct character haunted_wood = {
+	.name = "Haunted Wood",
+	.level = 30,
+	.reset = haunted_wood_reset,
+	.sprites = {
+		[CHARACTER_SPRITE_NORMAL] = &registry_sprites[REGISTRY_TEXTURE_HAUNTED_WOOD],
+	},
+	.exec = haunted_wood_strat
+};
+
+static struct character black_cat = {
+	.name = "Black Cat",
+	.level = 6,
+	.reset = black_cat_reset,
+	.sprites = {
+		[CHARACTER_SPRITE_NORMAL] = &registry_sprites[REGISTRY_TEXTURE_BLACK_CAT],
+	},
+	.exec = black_cat_strat
+};
+
+static void
+init(void)
+{
+	if (core_init("fr.malikania", "battle") < 0 || ui_init() < 0 || rpg_init() < 0)
+		panic();
+	if (window_open("Example - Battle", W, H) < 0)
+		panic();
+
+	registry_init();
+
+	/* Set cursor in default theme. */
+	theme_default()->sprites[THEME_SPRITE_CURSOR] = &registry_sprites[REGISTRY_TEXTURE_CURSOR];
+}
+
+static struct state fight_state;
+
+static void
+prepare_to_fight(void)
+{
+	struct battle *bt = alloc_new0(sizeof (*bt));
+
+//	bt->enemies[0].ch = &haunted_wood;
+	bt->team[0].ch = &team[0];
+	//bt->team[1].ch = &team[1];
+
+	/* Positionate the single ennemy to the left. */
+	align(ALIGN_LEFT,
+	    &bt->enemies[0].x, &bt->enemies[0].y,
+	    haunted_wood.sprites[CHARACTER_SPRITE_NORMAL]->cellw,
+	    haunted_wood.sprites[CHARACTER_SPRITE_NORMAL]->cellh,
+	    0, 0, window.w, window.h);
+
+	/* Black cat is near the previous monster. */
+	bt->enemies[1].ch = &black_cat;
+	bt->enemies[1].x = 500;
+	bt->enemies[1].y = 100;
+
+	bt->background = &registry_images[REGISTRY_IMAGE_BATTLE_BACKGROUND];
+
+	battle_start(bt);
+
+	fight_state.data = bt;
+	game_push(&fight_state);
+}
+
+
+static void
+empty_handle(struct state *st, const union event *ev)
+{
+	(void)st;
+
+	switch (ev->type) {
+	case EVENT_QUIT:
+		game_quit();
+		break;
+	case EVENT_KEYDOWN:
+		if (ev->key.key == KEY_SPACE)
+			prepare_to_fight();
+		break;
+	default:
+		break;
+	}
+}
+
+static void
+empty_draw(struct state *st)
+{
+	(void)st;
+
+	static const struct label info = {
+		.text = "Press <Space> to start a battle.",
+		.x = 10,
+		.y = 10,
+		.flags = LABEL_FLAGS_SHADOW
+	};
+
+	painter_set_color(0x4f8fbaff);
+	painter_clear();
+	label_draw(&info);
+	painter_present();
+}
+
+static struct state empty_state = {
+	.handle = empty_handle,
+	.draw = empty_draw
+};
+
+static void
+fight_handle(struct state *st, const union event *ev)
+{
+	battle_handle(st->data, ev);
+}
+
+static void
+fight_update(struct state *st, unsigned int ticks)
+{
+	struct battle *bt = st->data;
+
+	if (battle_update(bt, ticks))
+		game_push(&empty_state);
+}
+
+static void
+fight_draw(struct state *st)
+{
+	painter_set_color(0x000000ff);
+	painter_clear();
+	battle_draw(st->data);
+	painter_present();
+}
+
+static void
+fight_finish(struct state *st)
+{
+	battle_finish(st->data);
+	free(st->data);
+}
+
+static struct state fight_state = {
+	.handle = fight_handle,
+	.update = fight_update,
+	.draw = fight_draw,
+	.finish = fight_finish,
+};
+
+static void
+run(void)
+{
+	game_push(&empty_state);
+	game_loop();
+}
+
+static void
+quit(void)
+{
+	registry_finish();
+	theme_finish();
+	window_finish();
+	sys_finish();
+}
+
+int
+main(int argc, char **argv)
+{
+	--argc;
+	++argv;
+
+	init();
+	run();
+	quit();
+
+	return 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/example-battle/registry.c	Sat Oct 02 08:25:30 2021 +0200
@@ -0,0 +1,128 @@
+/*
+ * registry.h -- registry of resources
+ *
+ * Copyright (c) 2020-2021 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <stddef.h>
+
+#include <core/image.h>
+#include <core/panic.h>
+#include <core/util.h>
+#include <core/sys.h>
+
+#include "registry.h"
+
+#define PATH(r) util_pathf("%s/mlk-adventure/%s", sys_dir(SYS_DIR_DATA), r)
+
+struct texture registry_images[REGISTRY_IMAGE_NUM];
+struct texture registry_textures[REGISTRY_TEXTURE_NUM];
+struct sprite registry_sprites[REGISTRY_TEXTURE_NUM];
+struct sound registry_sounds[REGISTRY_SOUND_NUM];
+
+#define REGISTRY_IMAGE(i, path) \
+	{ (i), (path) }
+
+static const struct {
+	enum registry_image index;
+	const char *path;
+} images[] = {
+	REGISTRY_IMAGE(REGISTRY_IMAGE_BATTLE_BACKGROUND, "images/battle-background.png")
+};
+
+#define REGISTRY_TEXTURE(s, path, cw, ch) \
+	{ (s), (path), (cw), (ch) }
+
+static const struct {
+	enum registry_texture index;
+	const char *path;
+	unsigned int cellw;
+	unsigned int cellh;
+} textures[] = {
+	REGISTRY_TEXTURE(REGISTRY_TEXTURE_CURSOR, "sprites/ui-cursor.png", 24, 24),
+	REGISTRY_TEXTURE(REGISTRY_TEXTURE_EXPLOSION, "sprites/explosion.png", 256, 256),
+	REGISTRY_TEXTURE(REGISTRY_TEXTURE_JOHN_SWORD, "sprites/john-sword.png", 256, 256),
+	REGISTRY_TEXTURE(REGISTRY_TEXTURE_JOHN_WALK, "sprites/john-walk.png", 256, 256),
+	REGISTRY_TEXTURE(REGISTRY_TEXTURE_HAUNTED_WOOD, "images/haunted-wood.png", 0, 0),
+	REGISTRY_TEXTURE(REGISTRY_TEXTURE_BLACK_CAT, "images/black-cat.png", 0, 0)
+};
+
+#define REGISTRY_SOUND(s, path) \
+	{ (s), (path) }
+
+static const struct {
+	enum registry_sound index;
+	const char *path;
+} sounds[] = {
+	REGISTRY_SOUND(REGISTRY_SOUND_FIRE, "sounds/fire.wav")
+};
+
+static void
+load_images(void)
+{
+	for (size_t i = 0; i < UTIL_SIZE(images); ++i) {
+		struct texture *texture = &registry_images[images[i].index];
+
+		if (image_open(texture, PATH(images[i].path)) < 0)
+			panic();
+	}
+}
+
+static void
+load_textures_and_sprites(void)
+{
+	for (size_t i = 0; i < UTIL_SIZE(textures); ++i) {
+		struct texture *texture = &registry_textures[textures[i].index];
+		struct sprite *sprite = &registry_sprites[textures[i].index];
+
+		if (image_open(texture, PATH(textures[i].path)) < 0)
+			panic();
+
+		if (textures[i].cellw == 0 || textures[i].cellh == 0)
+			sprite_init(sprite, texture, texture->w, texture->h);
+		else
+			sprite_init(sprite, texture, textures[i].cellw, textures[i].cellh);
+	}
+}
+
+static void
+load_sounds(void)
+{
+	for (size_t i = 0; i < UTIL_SIZE(sounds); ++i) {
+		struct sound *sound = &registry_sounds[sounds[i].index];
+
+		if (sound_open(sound, PATH(sounds[i].path)) < 0)
+			panic();
+	}
+}
+
+void
+registry_init(void)
+{
+	load_images();
+	load_textures_and_sprites();
+	load_sounds();
+}
+
+void
+registry_finish(void)
+{
+	for (size_t i = 0; i < UTIL_SIZE(registry_images); ++i)
+		texture_finish(&registry_images[i]);
+	for (size_t i = 0; i < UTIL_SIZE(registry_textures); ++i)
+		texture_finish(&registry_textures[i]);
+	for (size_t i = 0; i < UTIL_SIZE(registry_sounds); ++i)
+		sound_finish(&registry_sounds[i]);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/example-battle/registry.h	Sat Oct 02 08:25:30 2021 +0200
@@ -0,0 +1,66 @@
+/*
+ * registry.h -- registry of resources
+ *
+ * Copyright (c) 2020-2021 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef EXAMPLES_BATTLE_REGISTRY_H
+#define EXAMPLES_BATTLE_REGISTRY_H
+
+#include <core/sound.h>
+#include <core/sprite.h>
+#include <core/texture.h>
+
+enum registry_texture {
+	/* UI. */
+	REGISTRY_TEXTURE_CURSOR,
+
+	/* Animations. */
+	REGISTRY_TEXTURE_EXPLOSION,
+
+	/* Characters. */
+	REGISTRY_TEXTURE_JOHN_WALK,
+	REGISTRY_TEXTURE_JOHN_SWORD,
+
+	/* Enemies. */
+	REGISTRY_TEXTURE_HAUNTED_WOOD,
+	REGISTRY_TEXTURE_BLACK_CAT,
+
+	/* Unused.*/
+	REGISTRY_TEXTURE_NUM
+};
+
+enum registry_image {
+	REGISTRY_IMAGE_BATTLE_BACKGROUND,
+	REGISTRY_IMAGE_NUM
+};
+
+enum registry_sound {
+	REGISTRY_SOUND_FIRE,
+	REGISTRY_SOUND_NUM
+};
+
+extern struct texture registry_images[REGISTRY_IMAGE_NUM];
+extern struct texture registry_textures[REGISTRY_TEXTURE_NUM];
+extern struct sprite registry_sprites[REGISTRY_TEXTURE_NUM];
+extern struct sound registry_sounds[REGISTRY_SOUND_NUM];
+
+void
+registry_init(void);
+
+void
+registry_finish(void);
+
+#endif /* !EXAMPLES_BATTLE_REGISTRY_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/example-battle/spell-fire.c	Sat Oct 02 08:25:30 2021 +0200
@@ -0,0 +1,129 @@
+/*
+ * spell-fire.c -- example of spell: fire
+ *
+ * Copyright (c) 2020-2021 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <stdlib.h>
+
+#include <core/action.h>
+#include <core/animation.h>
+#include <core/alloc.h>
+
+#include <ui/align.h>
+
+#include <rpg/battle.h>
+#include <rpg/character.h>
+#include <rpg/spell.h>
+
+#include "registry.h"
+#include "spell-fire.h"
+
+struct data {
+	struct battle *battle;
+	struct character *target;
+	struct animation animation;
+	struct action action;
+	unsigned int selection;
+};
+
+static int
+update(struct action *act, unsigned int ticks)
+{
+	struct data *data = act->data;
+
+	return animation_update(&data->animation, ticks);
+}
+
+static void
+draw(struct action *act)
+{
+	const struct data *data = act->data;
+	const struct battle_entity *et = &data->battle->enemies[data->selection];
+	const struct sprite *sprite = et->ch->sprites[CHARACTER_SPRITE_NORMAL];
+	int x, y;
+
+	align(ALIGN_CENTER,
+	    &x, &y, data->animation.sprite->cellw, data->animation.sprite->cellh,
+	    et->x, et->y, sprite->cellw, sprite->cellh);
+
+	animation_draw(&data->animation, x, y);
+}
+
+static void
+end(struct action *act)
+{
+	struct data *data = act->data;
+	struct character *ch = data->battle->enemies[data->selection].ch;
+
+	/* TODO: compute damage. */
+	const unsigned int damage = 100;
+
+	if ((unsigned int)ch->hp < damage)
+		ch->hp = 0;
+	else
+		ch->hp -= damage;
+
+	battle_indicator_hp(data->battle, data->battle->enemies[data->selection].ch, 100);
+}
+
+static void
+finish(struct action *act)
+{
+	free(act->data);
+}
+
+static void
+fire_select(const struct battle *bt, struct selection *slt)
+{
+	slt->index_side = 0;
+
+	selection_first(slt, bt);
+}
+
+static void
+fire_action(struct battle *bt, struct character *owner, const struct selection *slt)
+{
+	struct data *data;
+
+	(void)owner;
+
+	data = alloc_new0(sizeof (*data));
+	data->selection = slt->index_character;
+	data->battle = bt;
+	data->action.data = data;
+	data->action.update = update;
+	data->action.draw = draw;
+	data->action.finish = finish;
+	data->action.end = end;
+
+	animation_init(&data->animation, &registry_sprites[REGISTRY_TEXTURE_EXPLOSION], 12);
+	animation_start(&data->animation);
+
+	sound_play(&registry_sounds[REGISTRY_SOUND_FIRE], -1, 0);
+
+	action_stack_add(&bt->actions[0], &data->action);
+}
+
+const struct spell spell_fire = {
+	.name = "Fire",
+	.description = "A delicate fire.",
+	.mp = 5,
+	.type = SPELL_TYPE_FIRE,
+	.select = fire_select,
+	.select_kind = SELECTION_KIND_ONE,
+	.select_side = SELECTION_SIDE_ENEMY,
+	.action = fire_action
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/example-battle/spell-fire.h	Sat Oct 02 08:25:30 2021 +0200
@@ -0,0 +1,24 @@
+/*
+ * spell-fire.c -- example of spell: fire
+ *
+ * Copyright (c) 2020-2021 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef EXAMPLES_BATTLE_SPELL_FIRE_H
+#define EXAMPLES_BATTLE_SPELL_FIRE_H
+
+extern const struct spell spell_fire;
+
+#endif /* !EXAMPLES_BATTLE_SPELL_FIRE_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/example-cursor/main.c	Sat Oct 02 08:25:30 2021 +0200
@@ -0,0 +1,146 @@
+/*
+ * example-cursor.c -- example on how to change cursor
+ *
+ * Copyright (c) 2020-2021 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <stdio.h>
+
+#include <core/core.h>
+#include <core/event.h>
+#include <core/game.h>
+#include <core/key.h>
+#include <core/painter.h>
+#include <core/panic.h>
+#include <core/state.h>
+#include <core/sys.h>
+#include <core/util.h>
+#include <core/window.h>
+
+#include <ui/label.h>
+#include <ui/ui.h>
+
+#define W 1280
+#define H 720
+
+static char help_text[128];
+static enum window_cursor cursor = WINDOW_CURSOR_ARROW;
+
+static struct label help = {
+	.x = 10,
+	.y = 10,
+	.text = help_text,
+	.flags = LABEL_FLAGS_SHADOW
+};
+
+static void
+init(void)
+{
+	if (core_init("fr.malikania", "cursor") < 0 || ui_init() < 0)
+		panic();
+	if (window_open("Example - Cursor", W, H) < 0)
+		panic();
+}
+
+static void
+change(enum window_cursor cursor)
+{
+	static const char *names[] = {
+		[WINDOW_CURSOR_ARROW]           = "WINDOW_CURSOR_ARROW",
+		[WINDOW_CURSOR_EDIT]            = "WINDOW_CURSOR_EDIT",
+		[WINDOW_CURSOR_WAIT]            = "WINDOW_CURSOR_WAIT",
+		[WINDOW_CURSOR_CROSSHAIR]       = "WINDOW_CURSOR_CROSSHAIR",
+		[WINDOW_CURSOR_SIZE]            = "WINDOW_CURSOR_SIZE",
+		[WINDOW_CURSOR_NO]              = "WINDOW_CURSOR_NO",
+		[WINDOW_CURSOR_HAND]            = "WINDOW_CURSOR_HAND"
+	};
+
+	snprintf(help_text, sizeof (help_text), "Keys: <Left>/<Right> to change cursor. Current: %s", names[cursor]);
+	window_set_cursor(cursor);
+}
+
+static void
+handle(struct state *st, const union event *ev)
+{
+	(void)st;
+
+	switch (ev->type) {
+	case EVENT_KEYDOWN:
+		switch (ev->key.key) {
+		case KEY_LEFT:
+			if (cursor > 0)
+				change(--cursor);
+			break;
+		case KEY_RIGHT:
+			if (cursor + 1 < WINDOW_CURSOR_LAST)
+				change(++cursor);
+			break;
+		default:
+			break;
+		}
+
+
+		break;
+	case EVENT_QUIT:
+		game_quit();
+		break;
+	default:
+		break;
+	}
+}
+
+static void
+draw(struct state *st)
+{
+	(void)st;
+
+	painter_set_color(0xebede9ff);
+	painter_clear();
+	label_draw(&help);
+	painter_present();
+}
+
+static void
+run(void)
+{
+	struct state state = {
+		.handle = handle,
+		.draw = draw
+	};
+
+	change(cursor);
+
+	game_push(&state);
+	game_loop();
+}
+
+static void
+quit(void)
+{
+	window_finish();
+	ui_finish();
+	core_finish();
+}
+
+int
+main(int argc, char **argv)
+{
+	(void)argc;
+	(void)argv;
+
+	init();
+	run();
+	quit();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/example-debug/main.c	Sat Oct 02 08:25:30 2021 +0200
@@ -0,0 +1,111 @@
+/*
+ * example-debug.c -- example on how to use debug interface
+ *
+ * Copyright (c) 2020-2021 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <core/core.h>
+#include <core/event.h>
+#include <core/game.h>
+#include <core/window.h>
+#include <core/painter.h>
+#include <core/panic.h>
+#include <core/state.h>
+#include <core/util.h>
+
+#include <ui/debug.h>
+#include <ui/theme.h>
+#include <ui/ui.h>
+
+#define W 1280
+#define H 720
+
+static int mouse_x;
+static int mouse_y;
+
+static void
+init(void)
+{
+	if (core_init("fr.malikania", "debug") < 0 || ui_init() < 0)
+		panic();
+	if (window_open("Example - Debug", W, H) < 0)
+		panic();
+
+	debug_options.enable = 1;
+}
+
+static void
+handle(struct state *st, const union event *ev)
+{
+	(void)st;
+
+	switch (ev->type) {
+	case EVENT_MOUSE:
+		mouse_x = ev->mouse.x;
+		mouse_y = ev->mouse.y;
+		break;
+	case EVENT_QUIT:
+		game_quit();
+		break;
+	default:
+		break;
+	}
+}
+
+static void
+draw(struct state *st)
+{
+	(void)st;
+
+	struct debug_report report = {0};
+
+	painter_set_color(0x4f8fbaff);
+	painter_clear();
+	debugf(&report, "Game running.");
+	debugf(&report, "mouse: %d, %d", mouse_x, mouse_y);
+	painter_present();
+}
+
+static void
+run(void)
+{
+	struct state state = {
+		.handle = handle,
+		.draw = draw
+	};
+
+	game_push(&state);
+	game_loop();
+}
+
+static void
+quit(void)
+{
+	window_finish();
+	ui_finish();
+	core_finish();
+}
+
+int
+main(int argc, char **argv)
+{
+	(void)argc;
+	(void)argv;
+
+	init();
+	run();
+	quit();
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/example-drawable/main.c	Sat Oct 02 08:25:30 2021 +0200
@@ -0,0 +1,201 @@
+/*
+ * example-drawable.c -- example on how to use automatic drawables
+ *
+ * Copyright (c) 2020-2021 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <core/alloc.h>
+#include <core/animation.h>
+#include <core/core.h>
+#include <core/event.h>
+#include <core/game.h>
+#include <core/drawable.h>
+#include <core/key.h>
+#include <core/painter.h>
+#include <core/panic.h>
+#include <core/sys.h>
+#include <core/image.h>
+#include <core/sprite.h>
+#include <core/state.h>
+#include <core/texture.h>
+#include <core/util.h>
+#include <core/window.h>
+
+#include <ui/label.h>
+#include <ui/theme.h>
+#include <ui/ui.h>
+
+#define W       1280
+#define H       720
+#define PATH(r) util_pathf("%s/mlk-adventure/%s", sys_dir(SYS_DIR_DATA), r)
+
+static struct label help = {
+	.x = 10,
+	.y = 10,
+	.text = "Keys: <Esc> to reset. Click anywhere to spawn a drawable.",
+	.flags = LABEL_FLAGS_SHADOW
+};
+
+static struct drawable_stack stack;
+
+/*
+ * List of drawables for this example.
+ * -----------------------------------------------------------------------------
+ */
+
+/* 0: Explosion animation. */
+static struct texture explosion_tex;
+static struct sprite explosion_sprite;
+
+struct explosion {
+	struct animation anim;
+	struct drawable dw;
+};
+
+static void
+init(void)
+{
+	if (core_init("fr.malikania", "drawable") < 0 || ui_init() < 0)
+		panic();
+	if (window_open("Example - Drawable", W, H) < 0)
+		panic();
+
+	/* 0: Explosion animation. */
+	if (image_open(&explosion_tex, PATH("sprites/explosion.png")) < 0)
+		panic();
+
+	sprite_init(&explosion_sprite, &explosion_tex, 256, 256);
+}
+
+static int
+explosion_update(struct drawable *dw, unsigned int ticks)
+{
+	struct explosion *ex = dw->data;
+
+	return animation_update(&ex->anim, ticks);
+}
+
+static void
+explosion_draw(struct drawable *dw)
+{
+	struct explosion *ex = dw->data;
+
+	animation_draw(&ex->anim, ex->dw.x, ex->dw.y);
+}
+
+static void
+explosion_finish(struct drawable *dw)
+{
+	free(dw->data);
+}
+
+static void
+spawn(int x, int y)
+{
+	struct explosion *ex = alloc_new0(sizeof (*ex));
+
+	animation_init(&ex->anim, &explosion_sprite, 15);
+
+	ex->dw.data = ex;
+	ex->dw.x = x - (int)(explosion_sprite.cellw / 2);
+	ex->dw.y = y - (int)(explosion_sprite.cellh / 2);
+	ex->dw.update = explosion_update;
+	ex->dw.draw = explosion_draw;
+	ex->dw.finish = explosion_finish;
+
+	drawable_stack_add(&stack, &ex->dw);
+}
+
+static void
+handle(struct state *st, const union event *ev)
+{
+	(void)st;
+
+	switch (ev->type) {
+	case EVENT_KEYDOWN:
+		switch (ev->key.key) {
+		case KEY_ESCAPE:
+			drawable_stack_finish(&stack);
+			break;
+		default:
+			break;
+		}
+		break;
+	case EVENT_CLICKDOWN:
+		spawn(ev->click.x, ev->click.y);
+		break;
+	case EVENT_QUIT:
+		game_quit();
+		break;
+	default:
+		break;
+	}
+}
+
+static void
+update(struct state *st, unsigned int ticks)
+{
+	(void)st;
+
+	drawable_stack_update(&stack, ticks);
+
+}
+
+static void
+draw(struct state *st)
+{
+	(void)st;
+
+	painter_set_color(0xebede9ff);
+	painter_clear();
+	label_draw(&help);
+	drawable_stack_draw(&stack);
+	painter_present();
+}
+
+static void
+run(void)
+{
+	struct state state = {
+		.handle = handle,
+		.update = update,
+		.draw = draw
+	};
+
+	game_push(&state);
+	game_loop();
+}
+
+static void
+quit(void)
+{
+	window_finish();
+	ui_finish();
+	core_finish();
+}
+
+int
+main(int argc, char **argv)
+{
+	(void)argc;
+	(void)argv;
+
+	init();
+	run();
+	quit();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/example-font/main.c	Sat Oct 02 08:25:30 2021 +0200
@@ -0,0 +1,146 @@
+/*
+ * example-font.c -- show how to use fonts
+ *
+ * Copyright (c) 2020-2021 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <core/core.h>
+#include <core/event.h>
+#include <core/font.h>
+#include <core/game.h>
+#include <core/painter.h>
+#include <core/panic.h>
+#include <core/state.h>
+#include <core/sys.h>
+#include <core/texture.h>
+#include <core/util.h>
+#include <core/window.h>
+
+#include <ui/theme.h>
+#include <ui/ui.h>
+
+#define W       (1280)
+#define H       (720)
+
+/* Friendly taken from: https://lospec.com/palette-list/apollo */
+static const unsigned long colors[] = {
+	0x3c5e8bff,     /* Blue. */
+	0x468232ff,     /* Green. */
+	0xad7757ff,     /* Light brown. */
+	0x884b2bff,     /* Brown. */
+	0x752438ff,     /* Red. */
+	0x7a367bff,     /* Magenta. */
+	0x151d28ff,     /* Dark */
+	0xc7cfccff,     /* Christian Grey. */
+};
+
+static int ci = 0;
+static enum font_style style = FONT_STYLE_ANTIALIASED;
+
+static void
+init(void)
+{
+	if (core_init("fr.malikania", "font") < 0 || ui_init() < 0)
+		panic();
+	if (window_open("Example - Font", W, H) < 0)
+		panic();
+}
+
+static void
+handle(struct state *st, const union event *ev)
+{
+	(void)st;
+
+	switch (ev->type) {
+	case EVENT_KEYDOWN:
+		switch (ev->key.key) {
+		case KEY_LEFT:
+			if (ci > 0)
+				ci--;
+			break;
+		case KEY_RIGHT:
+			if ((size_t)ci < UTIL_SIZE(colors))
+				ci++;
+			break;
+		case KEY_SPACE:
+			if (style == FONT_STYLE_ANTIALIASED)
+				style = FONT_STYLE_NONE;
+			else
+				style = FONT_STYLE_ANTIALIASED;
+
+			theme_default()->fonts[THEME_FONT_INTERFACE]->style = style;
+		default:
+			break;
+		}
+		break;
+	case EVENT_QUIT:
+		game_quit();
+		break;
+	default:
+		break;
+	}
+}
+
+static void
+draw(struct state *st)
+{
+	(void)st;
+
+	struct font *font = theme_default()->fonts[THEME_FONT_INTERFACE];
+	struct texture tex;
+
+	painter_set_color(0xffffffff);
+	painter_clear();
+
+	if (font_render(font, &tex, "Example of text. Use <Left>/<Right> to change color and <Space> to toggle antialiasing.", colors[ci]) < 0)
+		panic();
+
+	texture_draw(&tex, 10, 10);
+	painter_present();
+	texture_finish(&tex);
+}
+
+static void
+run(void)
+{
+	struct state state = {
+		.handle = handle,
+		.draw = draw
+	};
+
+	game_push(&state);
+	game_loop();
+}
+
+static void
+quit(void)
+{
+	window_finish();
+	ui_finish();
+	core_finish();
+}
+
+int
+main(int argc, char **argv)
+{
+	(void)argc;
+	(void)argv;
+
+	init();
+	run();
+	quit();
+
+	return 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/example-gridmenu/main.c	Sat Oct 02 08:25:30 2021 +0200
@@ -0,0 +1,139 @@
+/*
+ * example-gridmenu.c -- show how to use grid menu
+ *
+ * Copyright (c) 2020-2021 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <core/core.h>
+#include <core/event.h>
+#include <core/game.h>
+#include <core/painter.h>
+#include <core/panic.h>
+#include <core/state.h>
+#include <core/sys.h>
+#include <core/trace.h>
+#include <core/util.h>
+#include <core/window.h>
+
+#include <ui/align.h>
+#include <ui/gridmenu.h>
+#include <ui/label.h>
+#include <ui/theme.h>
+#include <ui/ui.h>
+
+#define W       (1280)
+#define H       (720)
+
+static void
+init(void)
+{
+	if (core_init("fr.malikania", "grid-menu") < 0 || ui_init() < 0)
+		panic();
+	if (window_open("Example - Grid menu", W, H) < 0)
+		panic();
+}
+
+static void
+quit(void)
+{
+	window_finish();
+	ui_finish();
+	core_finish();
+}
+
+static void
+handle(struct state *st, const union event *ev)
+{
+	switch (ev->type) {
+	case EVENT_QUIT:
+		game_quit();
+		break;
+	default:
+		gridmenu_handle(st->data, ev);
+		break;
+	}
+}
+
+static void
+update(struct state *st, unsigned int ticks)
+{
+	(void)ticks;
+
+	struct gridmenu *menu = st->data;
+
+	if (menu->state == GRIDMENU_STATE_ACTIVATED) {
+		tracef("selected index: %u", (unsigned int)menu->selected);
+		gridmenu_reset(menu);
+	}
+}
+
+static void
+draw(struct state *st)
+{
+	painter_set_color(0x4f8fbaff);
+	painter_clear();
+	gridmenu_draw(st->data);
+	painter_present();
+}
+
+static void
+run(void)
+{
+	struct gridmenu menu = {
+		.menu = {
+			"Feu mineur",
+			"Feu majeur",
+			"Feu septième",
+			"Glace mineure",
+			"Glace majeure",
+			"Glace septième",
+			"Foudre mineure",
+			"Foudre majeure",
+			"Foudre septième",
+			"Choc mineur",
+			"Choc majeur",
+			"Choc septième",
+		},
+		.w = 300,
+		.h = 100,
+		.nrows = 3,
+		.ncols = 2
+	};
+	struct state state = {
+		.data = &menu,
+		.handle = handle,
+		.update = update,
+		.draw = draw,
+	};
+
+	align(ALIGN_CENTER, &menu.x, &menu.y, menu.w, menu.h, 0, 0, W, H);
+
+	/* Need to repaint at least once. */
+	gridmenu_repaint(&menu);
+
+	game_push(&state);
+	game_loop();
+}
+
+int
+main(int argc, char **argv)
+{
+	(void)argc;
+	(void)argv;
+
+	init();
+	run();
+	quit();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/example-label/main.c	Sat Oct 02 08:25:30 2021 +0200
@@ -0,0 +1,183 @@
+/*
+ * example-label.c -- show how to use label and alignments
+ *
+ * Copyright (c) 2020-2021 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <stddef.h>
+
+#include <core/core.h>
+#include <core/event.h>
+#include <core/game.h>
+#include <core/painter.h>
+#include <core/panic.h>
+#include <core/state.h>
+#include <core/sys.h>
+#include <core/util.h>
+#include <core/window.h>
+
+#include <ui/align.h>
+#include <ui/label.h>
+#include <ui/theme.h>
+#include <ui/ui.h>
+
+#define W       (1280)
+#define H       (720)
+
+struct {
+	enum align align;
+	struct label label;
+} table[] = {
+	{
+		.align = ALIGN_TOP_LEFT,
+		.label = {
+			.text = "Top left"
+		}
+	},
+	{
+		.align = ALIGN_TOP,
+		.label = {
+			.text = "Top",
+		}
+	},
+	{
+		.align = ALIGN_TOP_RIGHT,
+		.label = {
+			.text = "Top right",
+		}
+	},
+	{
+		.align = ALIGN_RIGHT,
+		.label = {
+			.text = "Right",
+		}
+	},
+	{
+		.align = ALIGN_BOTTOM_RIGHT,
+		.label = {
+			.text = "Bottom right",
+		}
+	},
+	{
+		.align = ALIGN_BOTTOM,
+		.label = {
+			.text = "Bottom",
+		}
+	},
+	{
+		.align = ALIGN_BOTTOM_LEFT,
+		.label = {
+			.text = "Bottom left",
+		}
+	},
+	{
+		.align = ALIGN_LEFT,
+		.label = {
+			.text = "Left",
+		}
+	},
+	{
+		.align = ALIGN_CENTER,
+		.label = {
+			.text = "The world is Malikania.",
+			.flags = LABEL_FLAGS_SHADOW
+		}
+	}
+};
+
+static struct label mlabel = {
+	.text = "This one follows your mouse and is not aligned."
+};
+
+static void
+init(void)
+{
+	if (core_init("fr.malikania", "label") < 0 || ui_init() < 0)
+		panic();
+	if (window_open("Example - Label", W, H) < 0)
+		panic();
+
+	for (size_t i = 0; i < UTIL_SIZE(table); ++i) {
+		struct label *l = &table[i].label;
+		unsigned int w, h;
+
+		label_query(l, &w, &h);
+		align(table[i].align, &l->x, &l->y, w, h, 0, 0, W, H);
+	}
+}
+
+static void
+quit(void)
+{
+	window_finish();
+	ui_finish();
+	core_finish();
+}
+
+static void
+handle(struct state *st, const union event *ev)
+{
+	(void)st;
+
+	switch (ev->type) {
+	case EVENT_MOUSE:
+		mlabel.x = ev->mouse.x;
+		mlabel.y = ev->mouse.y;
+		break;
+	case EVENT_QUIT:
+		game_quit();
+		break;
+	default:
+		break;
+	}
+}
+
+static void
+draw(struct state *st)
+{
+	(void)st;
+
+	painter_set_color(0x4f8fbaff);
+	painter_clear();
+
+	for (size_t i = 0; i < UTIL_SIZE(table); ++i)
+		label_draw(&table[i].label);
+
+	label_draw(&mlabel);
+	painter_present();
+}
+
+static void
+run(void)
+{
+	struct state state = {
+		.handle = handle,
+		.draw = draw
+	};
+
+	game_push(&state);
+	game_loop();
+}
+
+int
+main(int argc, char **argv)
+{
+	(void)argc;
+	(void)argv;
+
+	init();
+	run();
+	quit();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/example-message/main.c	Sat Oct 02 08:25:30 2021 +0200
@@ -0,0 +1,327 @@
+/*
+ * example-message.c -- show how to use messages
+ *
+ * Copyright (c) 2020-2021 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <stddef.h>
+
+#include <core/core.h>
+#include <core/event.h>
+#include <core/game.h>
+#include <core/painter.h>
+#include <core/panic.h>
+#include <core/state.h>
+#include <core/sys.h>
+#include <core/util.h>
+#include <core/window.h>
+
+#include <ui/frame.h>
+#include <ui/theme.h>
+#include <ui/ui.h>
+
+#include <rpg/message.h>
+#include <rpg/rpg.h>
+
+#define W       (1280)
+#define H       (720)
+
+#define MW      (W * 0.75)
+#define MH      (H * 0.120)
+#define MX      ((W / 2) - (MW / 2))
+#define MY      (100)
+
+static void
+init(void)
+{
+	if (core_init("fr.malikania", "message") < 0 || ui_init() < 0 || rpg_init() < 0)
+		panic();
+	if (window_open("Example - Message", W, H) < 0)
+		panic();
+}
+
+static void
+quit(void)
+{
+	window_finish();
+	rpg_finish();
+	ui_finish();
+	core_finish();
+}
+
+static void
+handle(struct state *st, const union event *ev)
+{
+	switch (ev->type) {
+	case EVENT_QUIT:
+		game_quit();
+		break;
+	default:
+		message_handle(st->data, ev);
+		break;
+	}
+}
+
+static void
+update(struct state *st, unsigned int ticks)
+{
+	if (message_update(st->data, ticks))
+		game_quit();
+}
+
+static void
+draw(struct state *st)
+{
+	painter_set_color(0xffffffff);
+	painter_clear();
+	message_draw(st->data);
+	painter_present();
+}
+
+static void
+run(struct message *msg)
+{
+	struct state state = {
+		.data = msg,
+		.handle = handle,
+		.update = update,
+		.draw = draw
+	};
+
+	message_start(msg);
+
+	game_push(&state);
+	game_loop();
+}
+
+static void
+my_draw_frame(const struct theme *th, const struct frame *f)
+{
+	(void)th;
+
+	painter_set_color(0xff0000ff);
+	painter_draw_rectangle(f->x, f->y, f->w, f->h);
+}
+
+static void
+basic(void)
+{
+	struct message msg = {
+		.x = MX,
+		.y = MY,
+		.w = MW,
+		.spacing = 12,
+		.text = {
+			"This is a basic message.",
+			"Vertical spacing is automatically computed.",
+			"You need to press <Enter> to close it.",
+		},
+	};
+
+	message_query(&msg, NULL, &msg.h);
+	run(&msg);
+}
+
+static void
+automatic(void)
+{
+	struct message msg = {
+		.x = MX,
+		.y = MY,
+		.w = MW,
+		.timeout = MESSAGE_TIMEOUT_DEFAULT,
+		.text = {
+			"This is a an automatic message.",
+			"It will disappear in a few seconds.",
+			"You can still press <Enter> to close it quicker."
+		},
+		.flags = MESSAGE_FLAGS_AUTOMATIC
+	};
+
+	message_query(&msg, NULL, &msg.h);
+	run(&msg);
+}
+
+static void
+fadein(void)
+{
+	struct message msg = {
+		.x = MX,
+		.y = MY,
+		.w = MW,
+		.delay = MESSAGE_DELAY_DEFAULT,
+		.text = {
+			"This message will fade in."
+		},
+		.flags = MESSAGE_FLAGS_FADEIN
+	};
+
+	message_query(&msg, NULL, &msg.h);
+	run(&msg);
+}
+
+static void
+fadeout(void)
+{
+	struct message msg = {
+		.x = MX,
+		.y = MY,
+		.w = MW,
+		.delay = MESSAGE_DELAY_DEFAULT,
+		.text = {
+			"This message will fade out."
+		},
+		.flags = MESSAGE_FLAGS_FADEOUT
+	};
+
+	message_query(&msg, NULL, &msg.h);
+	run(&msg);
+}
+
+static void
+fade(void)
+{
+	struct message msg = {
+		.x = MX,
+		.y = MY,
+		.w = MW,
+		.delay = MESSAGE_DELAY_DEFAULT,
+		.text = {
+			"This message will fade in and out."
+		},
+		.flags = MESSAGE_FLAGS_FADEIN | MESSAGE_FLAGS_FADEOUT
+	};
+
+	message_query(&msg, NULL, &msg.h);
+	run(&msg);
+}
+
+static void
+question(void)
+{
+	struct message msg = {
+		.x = MX,
+		.y = MY,
+		.w = MW,
+		.text = {
+			"Okay, I've understood.",
+			"Nevermind, I'll do it again."
+		},
+		.flags = MESSAGE_FLAGS_QUESTION
+	};
+
+	message_query(&msg, NULL, &msg.h);
+	run(&msg);
+}
+
+static void
+smallbottom(void)
+{
+	const unsigned int w = window.w / 4;
+	const unsigned int h = MH;
+	const int x = (window.w / 2) - (w / 2);
+	const int y = (window.h - h - 10);
+
+	struct message msg = {
+		.x = x,
+		.y = y,
+		.w = w,
+		.h = h,
+		.delay = MESSAGE_DELAY_DEFAULT,
+		.flags = MESSAGE_FLAGS_FADEIN | MESSAGE_FLAGS_FADEOUT,
+		.text = {
+			"This one is small here."
+		}
+	};
+
+	run(&msg);
+}
+
+static void
+toosmallh(void)
+{
+	struct message msg = {
+		.x = MX,
+		.y = MY,
+		.w = MW,
+		.h = 40,
+		.text = {
+			"This one is too small in height and will emit a warning.",
+			"Because this line will be incomplete."
+		},
+	};
+
+	run(&msg);
+}
+
+static void
+toosmallw(void)
+{
+	struct message msg = {
+		.x = MX,
+		.y = MY,
+		.w = 160,
+		.h = MH,
+		.text = {
+			"This one is too small in width."
+		},
+	};
+
+	run(&msg);
+}
+
+static void
+custom(void)
+{
+	struct theme theme;
+	struct message msg = {
+		.x = MX,
+		.y = MY,
+		.w = MW,
+		.h = MH,
+		.text = {
+			"This one will destroy your eyes.",
+			"Because it use a terrible custom theme."
+		},
+		.theme = &theme
+	};
+
+	/* Borrow default theme and change its frame drawing. */
+	theme_shallow(&theme, NULL);
+	theme.draw_frame = my_draw_frame;
+	theme.colors[THEME_COLOR_NORMAL] = 0x0000ffff;
+
+	run(&msg);
+}
+
+int
+main(int argc, char **argv)
+{
+	(void)argc;
+	(void)argv;
+
+	init();
+	basic();
+	fadein();
+	fadeout();
+	fade();
+	automatic();
+	question();
+	smallbottom();
+	toosmallh();
+	toosmallw();
+	custom();
+	quit();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/example-sprite/main.c	Sat Oct 02 08:25:30 2021 +0200
@@ -0,0 +1,161 @@
+/*
+ * example-sprite.c -- example on how to use sprites
+ *
+ * Copyright (c) 2020-2021 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <stdio.h>
+
+#include <core/core.h>
+#include <core/event.h>
+#include <core/game.h>
+#include <core/image.h>
+#include <core/key.h>
+#include <core/painter.h>
+#include <core/panic.h>
+#include <core/image.h>
+#include <core/sprite.h>
+#include <core/state.h>
+#include <core/sys.h>
+#include <core/texture.h>
+#include <core/util.h>
+#include <core/window.h>
+
+#include <ui/align.h>
+#include <ui/label.h>
+#include <ui/ui.h>
+
+#define W       1280
+#define H       720
+#define PATH(r) util_pathf("%s/mlk-adventure/%s", sys_dir(SYS_DIR_DATA), r)
+#define HEADER "Keys: <Left>/<Right> and <Up/Down> to select a column/row. Current: %u, %u (total %u/%u)"
+
+static char msg[512];
+static struct texture texture;
+static struct sprite sprite;
+static unsigned int row, column;
+
+static struct label help = {
+	.x = 10,
+	.y = 10,
+	.text = msg,
+	.flags = LABEL_FLAGS_SHADOW
+};
+
+static void
+changed(void)
+{
+	snprintf(msg, sizeof (msg), HEADER, column, row, sprite.ncols, sprite.nrows);
+}
+
+static void
+init(void)
+{
+	if (core_init("fr.malikania", "sprite") < 0 || ui_init() < 0)
+		panic();
+	if (window_open("Example - Sprite", W, H) < 0)
+		panic();
+	if (image_open(&texture, PATH("sprites/people.png")) < 0)
+		panic();
+
+	sprite_init(&sprite, &texture, 48, 48);
+}
+
+static void
+handle(struct state *st, const union event *ev)
+{
+	(void)st;
+
+	switch (ev->type) {
+	case EVENT_KEYDOWN:
+		switch (ev->key.key) {
+		case KEY_LEFT:
+			if (column > 0)
+				column--;
+			break;
+		case KEY_RIGHT:
+			if (column + 1 < sprite.ncols)
+				column++;
+			break;
+		case KEY_UP:
+			if (row > 0)
+				row--;
+			break;
+		case KEY_DOWN:
+			if (row + 1 < sprite.nrows)
+				row++;
+			break;
+		default:
+			break;
+		}
+
+		changed();
+		break;
+	case EVENT_QUIT:
+		game_quit();
+		break;
+	default:
+		break;
+	}
+}
+
+static void
+draw(struct state *st)
+{
+	(void)st;
+
+	int x, y;
+
+	painter_set_color(0xebede9ff);
+	painter_clear();
+	align(ALIGN_CENTER, &x, &y, sprite.cellw, sprite.cellh, 0, 0, W, H);
+	sprite_draw(&sprite, row, column, x, y);
+	label_draw(&help);
+	painter_present();
+}
+
+static void
+run(void)
+{
+	struct state state = {
+		.handle = handle,
+		.draw = draw
+	};
+
+	changed();
+
+	game_push(&state);
+	game_loop();
+}
+
+static void
+quit(void)
+{
+	texture_finish(&texture);
+	window_finish();
+	ui_finish();
+	core_finish();
+}
+
+int
+main(int argc, char **argv)
+{
+	(void)argc;
+	(void)argv;
+
+	init();
+	run();
+	quit();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/example-trace/main.c	Sat Oct 02 08:25:30 2021 +0200
@@ -0,0 +1,126 @@
+/*
+ * example-trace.c -- example on how to use custom trace handlers
+ *
+ * Copyright (c) 2020-2021 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <core/core.h>
+#include <core/event.h>
+#include <core/game.h>
+#include <core/sys.h>
+#include <core/window.h>
+#include <core/painter.h>
+#include <core/panic.h>
+#include <core/state.h>
+#include <core/trace.h>
+#include <core/util.h>
+
+#include <ui/theme.h>
+#include <ui/ui.h>
+
+#include <adventure/trace_hud.h>
+
+#define W 1280
+#define H 720
+
+static void
+init(void)
+{
+	if (core_init("fr.malikania", "trace") < 0 || ui_init() < 0)
+		panic();
+	if (window_open("Example - Trace", W, H) < 0)
+		panic();
+
+	trace_handler = trace_hud_handler;
+}
+
+static void
+handle(struct state *st, const union event *ev)
+{
+	(void)st;
+
+	switch (ev->type) {
+	case EVENT_KEYDOWN:
+		switch (ev->key.key) {
+		case KEY_ESCAPE:
+			trace_hud_clear();
+			break;
+		default:
+			tracef("keydown pressed: %d", ev->key.key);
+			break;
+		}
+		break;
+	case EVENT_CLICKDOWN:
+		tracef("click at %d,%d", ev->click.x, ev->click.y);
+		break;
+	case EVENT_QUIT:
+		game_quit();
+		break;
+	default:
+		break;
+	}
+}
+
+static void
+update(struct state *st, unsigned int ticks)
+{
+	(void)st;
+
+	trace_hud_update(ticks);
+}
+
+static void
+draw(struct state *st)
+{
+	(void)st;
+
+	painter_set_color(0x4f8fbaff);
+	painter_clear();
+	trace_hud_draw();
+	painter_present();
+}
+
+static void
+run(void)
+{
+	struct state state = {
+		.handle = handle,
+		.update = update,
+		.draw = draw
+	};
+
+	game_push(&state);
+	game_loop();
+}
+
+static void
+quit(void)
+{
+	window_finish();
+	ui_finish();
+	core_finish();
+}
+
+int
+main(int argc, char **argv)
+{
+	(void)argc;
+	(void)argv;
+
+	init();
+	run();
+	quit();
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/example-ui/main.c	Sat Oct 02 08:25:30 2021 +0200
@@ -0,0 +1,324 @@
+/*
+ * example-action.c -- example on how to use automatic actions
+ *
+ * Copyright (c) 2020-2021 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <core/action.h>
+#include <core/core.h>
+#include <core/event.h>
+#include <core/game.h>
+#include <core/maths.h>
+#include <core/panic.h>
+#include <core/painter.h>
+#include <core/state.h>
+#include <core/sys.h>
+#include <core/util.h>
+#include <core/window.h>
+
+#include <ui/align.h>
+#include <ui/button.h>
+#include <ui/checkbox.h>
+#include <ui/frame.h>
+#include <ui/label.h>
+#include <ui/theme.h>
+#include <ui/ui.h>
+
+#define W               (1280)
+#define H               (720)
+
+#define FRAME_ORIGIN_X  (10)
+#define FRAME_ORIGIN_Y  (10)
+#define FRAME_WIDTH     (400)
+#define FRAME_HEIGHT    (200)
+
+#define HEADER_HEIGHT   (32)
+
+#define ELEMENT_HEIGHT  (20)
+
+/*
+ * We design a basic UI like this.
+ *
+ *                    FRAME_WIDTH
+ * +---------------------------------------------+--
+ * | Title                                       | | HEADER_HEIGHT
+ * +---------------------------------------------+--
+ * | [x] Auto save                               |
+ * |                                             |
+ * |                                             |
+ * |                                    [ Quit ] |
+ * +---------------------------------------------+
+ */
+static struct {
+	struct action_stack st;
+
+	struct {
+		int active;
+		int x;
+		int y;
+	} motion;
+
+	struct {
+		struct frame frame;
+		struct action act;
+	} panel;
+
+	struct {
+		struct label label;
+		struct action act;
+	} header;
+
+	struct {
+		struct checkbox cb;
+		struct action cb_act;
+		struct label label;
+		struct action label_act;
+	} autosave;
+
+	struct {
+		struct button button;
+		struct action act;
+	} quit;
+} ui = {
+	.panel = {
+		.frame = {
+			.x = FRAME_ORIGIN_X,
+			.y = FRAME_ORIGIN_Y,
+			.w = FRAME_WIDTH,
+			.h = FRAME_HEIGHT
+		}
+	},
+	.header = {
+		.label = {
+			.text = "Preferences",
+			.x = FRAME_ORIGIN_X,
+			.y = FRAME_ORIGIN_Y,
+			.flags = LABEL_FLAGS_SHADOW,
+		}
+	},
+	.autosave = {
+		.cb = {
+			.w = ELEMENT_HEIGHT,
+			.h = ELEMENT_HEIGHT
+		},
+		.label = {
+			.text = "Auto save game",
+			.flags = LABEL_FLAGS_SHADOW,
+		}
+	},
+	.quit = {
+		.button = {
+			.text = "Quit",
+			.h = ELEMENT_HEIGHT
+		}
+	}
+};
+
+static void
+init(void)
+{
+	if (core_init("fr.malikania", "ui") < 0 || ui_init() < 0)
+		panic();
+	if (window_open("Example - UI", W, H) < 0)
+		panic();
+}
+
+static void
+resize_header(void)
+{
+	struct frame *f = &ui.panel.frame;
+	struct label *l = &ui.header.label;
+	unsigned int w, h;
+
+	/* Header. */
+	label_query(l, &w, &h);
+	align(ALIGN_LEFT, &l->x, &l->y, w, h, f->x, f->y, f->w, HEADER_HEIGHT);
+
+	l->x += theme_default()->padding;
+}
+
+static void
+resize_autosave(void)
+{
+	unsigned int padding = theme_default()->padding;
+	struct frame *f = &ui.panel.frame;
+	struct checkbox *c = &ui.autosave.cb;
+	struct label *l = &ui.autosave.label;
+
+	c->x = f->x + padding;
+	c->y = f->y + HEADER_HEIGHT + padding;
+
+	l->x = c->x + c->w + padding;
+	l->y = c->y;
+}
+
+static void
+resize_button(void)
+{
+	unsigned int padding = theme_default()->padding;
+	struct frame *f = &ui.panel.frame;
+	struct button *b = &ui.quit.button;
+
+	/* Button. */
+	b->w = f->w / 4;
+
+	align(ALIGN_BOTTOM_RIGHT, &b->x, &b->y, b->w, b->h,
+	    f->x, f->y, f->w, f->h);
+
+	b->x -= padding;
+	b->y -= padding;
+}
+
+static void
+resize(void)
+{
+	resize_header();
+	resize_autosave();
+	resize_button();
+}
+
+static void
+prepare(void)
+{
+	/* Frame. */
+	frame_action(&ui.panel.frame, &ui.panel.act);
+
+	/* Header title. */
+	label_action(&ui.header.label, &ui.header.act);
+
+	/* Button quit. */
+	button_action(&ui.quit.button, &ui.quit.act);
+
+	/* Autosave. */
+	checkbox_action(&ui.autosave.cb, &ui.autosave.cb_act);
+	label_action(&ui.autosave.label, &ui.autosave.label_act);
+
+	/* Add all UI elements. */
+	action_stack_add(&ui.st, &ui.panel.act);
+	action_stack_add(&ui.st, &ui.header.act);
+	action_stack_add(&ui.st, &ui.autosave.cb_act);
+	action_stack_add(&ui.st, &ui.autosave.label_act);
+	action_stack_add(&ui.st, &ui.quit.act);
+}
+
+static int
+headerclick(int x, int y)
+{
+	return maths_is_boxed(
+	    ui.panel.frame.x,
+	    ui.panel.frame.y,
+	    ui.panel.frame.w,
+	    HEADER_HEIGHT,
+	    x,
+	    y
+	);
+}
+
+static void
+handle(struct state *st, const union event *ev)
+{
+	(void)st;
+
+	switch (ev->type) {
+	case EVENT_QUIT:
+		game_quit();
+		break;
+	case EVENT_MOUSE:
+		if (ui.motion.active) {
+			ui.panel.frame.x += ev->mouse.x - ui.motion.x;
+			ui.panel.frame.y += ev->mouse.y - ui.motion.y;
+			ui.motion.x = ev->mouse.x;
+			ui.motion.y = ev->mouse.y;
+			resize();
+		}
+		break;
+	case EVENT_CLICKDOWN:
+		if (headerclick(ev->click.x, ev->click.y)) {
+			ui.motion.active = 1;
+			ui.motion.x = ev->click.x;
+			ui.motion.y = ev->click.y;
+			window_set_cursor(WINDOW_CURSOR_SIZE);
+		}
+		else
+			action_stack_handle(&ui.st, ev);
+		break;
+	case EVENT_CLICKUP:
+		ui.motion.active = 0;
+		window_set_cursor(WINDOW_CURSOR_ARROW);
+		/* Fallthrough. */
+	default:
+		action_stack_handle(&ui.st, ev);
+		break;
+	}
+}
+
+static void
+update(struct state *st, unsigned int ticks)
+{
+	(void)st;
+
+	if (ui.quit.button.state == BUTTON_STATE_ACTIVATED)
+		game_quit();
+	else
+		action_stack_update(&ui.st, ticks);
+}
+
+static void
+draw(struct state *st)
+{
+	(void)st;
+
+	painter_set_color(0xffffffff);
+	painter_clear();
+	action_stack_draw(&ui.st);
+	painter_present();
+}
+
+static void
+run(void)
+{
+	struct state state = {
+		.handle = handle,
+		.update = update,
+		.draw = draw
+	};
+
+	prepare();
+	resize();
+
+	game_push(&state);
+	game_loop();
+}
+
+static void
+quit(void)
+{
+	window_finish();
+	ui_finish();
+	core_finish();
+}
+
+int
+main(int argc, char **argv)
+{
+	(void)argc;
+	(void)argv;
+
+	init();
+	run();
+	quit();
+
+	return 0;
+}
--- a/src/examples/example-action/main.c	Fri Oct 01 15:04:00 2021 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,399 +0,0 @@
-/*
- * example-action.c -- example on how to use automatic actions
- *
- * Copyright (c) 2020-2021 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <assert.h>
-
-#include <core/action.h>
-#include <core/core.h>
-#include <core/event.h>
-#include <core/game.h>
-#include <core/image.h>
-#include <core/maths.h>
-#include <core/painter.h>
-#include <core/panic.h>
-#include <core/script.h>
-#include <core/sprite.h>
-#include <core/state.h>
-#include <core/sys.h>
-#include <core/texture.h>
-#include <core/util.h>
-#include <core/window.h>
-
-#include <ui/theme.h>
-#include <ui/ui.h>
-
-#include <rpg/message.h>
-#include <rpg/rpg.h>
-
-#define W       1280
-#define H       720
-#define PATH(r) util_pathf("%s/mlk-adventure/%s", sys_dir(SYS_DIR_DATA), r)
-
-#define MW      (W * 0.75)
-#define MX      ((W / 2) - (MW / 2))
-#define MY      (100)
-
-/* This is a stack of "parallel" events. */
-static struct action_stack events;
-
-/* This is a stack of modal events. */
-static struct action_stack modal;
-
-/*
- * Event object for the chest to click on.
- */
-static struct {
-	struct message msg;
-	struct action msg_act;
-	int x;
-	int y;
-	int opened;
-	struct texture image;
-	struct sprite sprite;
-	struct action event;
-} chest = {
-	.msg = {
-		.x = MX,
-		.y = MY,
-		.w = MW,
-		.text = {
-			"100000 pièces.",
-			"Te voilà riche sale file de crevette."
-		}
-	}
-};
-
-/*
- * Event object for the guide who ask dialog with you.
- *
- * It is a script spawned upon click like this.
- *
- * [guide.event]
- *       |           spawn
- *   (on click)------------------->[message 1]
- *                                      v
- *                                 [message 2]
- *                                      v
- *                                 [question 3]
- *                   spawn              v
- * [response]<---------------------[check result]
- *     v
- * [message 4/5]
- */
-static struct {
-	struct {
-		struct message msg;
-		struct action act;
-	} msgs[5];
-
-	/* This is the sprite and action to click on. */
-	int x;
-	int y;
-	struct texture image;
-	struct sprite sprite;
-	struct action event;
-
-	/* This is the event for the response. */
-	struct action response;
-
-	struct script script;
-	struct action script_act;
-} guide = {
-	.msgs = {
-		{
-			.msg = {
-				.x = MX,
-				.y = MY,
-				.w = MW,
-				.delay = MESSAGE_DELAY_DEFAULT,
-				.flags = MESSAGE_FLAGS_FADEIN,
-				.text = {
-					"Bienvenue dans ce monde Molko."
-				}
-			},
-		},
-		{
-			.msg = {
-				.x = MX,
-				.y = MY,
-				.w = MW,
-				.text = {
-					"Penses tu vraiment pouvoir me battre ?"
-				}
-			}
-		},
-		{
-			.msg = {
-				.x = MX,
-				.y = MY,
-				.w = MW,
-				.flags = MESSAGE_FLAGS_QUESTION,
-				.text = {
-					"Non j'ai la trouille.",
-					"Bien sûr, je suis la légende."
-				}
-			}
-		},
-
-		/* In case of NO. */
-		{
-			.msg = {
-				.x = MX,
-				.y = MY,
-				.w = MW,
-				.text = {
-					"Poule mouillée."
-				}
-			}
-		},
-
-		/* In case of YES. */
-		{
-			.msg = {
-				.x = MX,
-				.y = MY,
-				.w = MW,
-				.text = {
-					"Prépare toi à souffrir."
-				}
-			}
-		}
-	}
-};
-
-static int
-guide_response_update(struct action *act, unsigned int ticks)
-{
-	/* Immediately return to get access to end. */
-	(void)act;
-	(void)ticks;
-
-	return 1;
-}
-
-static void
-guide_response_end(struct action *act)
-{
-	(void)act;
-
-	/* We add a final response depending on the result. */
-	const int index = guide.msgs[2].msg.index + 3;
-
-	message_action(&guide.msgs[index].msg, &guide.msgs[index].act);
-	message_query(&guide.msgs[index].msg, NULL, &guide.msgs[index].msg.h);
-	action_stack_add(&modal, &guide.msgs[index].act);
-}
-
-static void
-guide_popup(void)
-{
-	/* Prepare the script with first 3 messages. */
-	for (size_t i = 0; i < 3; ++i) {
-		message_action(&guide.msgs[i].msg, &guide.msgs[i].act);
-		message_query(&guide.msgs[i].msg, NULL, &guide.msgs[i].msg.h);
-		script_append(&guide.script, &guide.msgs[i].act);
-	}
-
-	/* Reset the message index. */
-	guide.msgs[2].msg.index = 0;
-
-	/* This is final action that analyze user input. */
-	guide.response.update = guide_response_update;
-	guide.response.end = guide_response_end;
-	script_append(&guide.script, &guide.response);
-
-	script_action(&guide.script, &guide.script_act);
-	action_stack_add(&modal, &guide.script_act);
-}
-
-static void
-guide_handle(struct action *act, const union event *ev)
-{
-	(void)act;
-
-	if (!action_stack_completed(&modal))
-		return;
-
-	switch (ev->type) {
-	case EVENT_CLICKDOWN:
-		if (maths_is_boxed(guide.x, guide.y,
-		    guide.sprite.cellw, guide.sprite.cellh,
-		    ev->click.x, ev->click.y))
-			guide_popup();
-		break;
-	default:
-		break;
-	}
-}
-
-static void
-guide_draw(struct action *act)
-{
-	(void)act;
-
-	sprite_draw(&guide.sprite, 0, 0, guide.x, guide.y);
-}
-
-static void
-guide_init(void)
-{
-	if (image_open(&guide.image, PATH("sprites/people.png")) < 0)
-		panic();
-
-	sprite_init(&guide.sprite, &guide.image, 48, 48);
-
-	/* This is the global guide action. */
-	guide.event.handle = guide_handle;
-	guide.event.draw = guide_draw;
-	guide.x = 200;
-	guide.y = 200;
-}
-
-static void
-chest_handle(struct action *act, const union event *ev)
-{
-	(void)act;
-
-	if (chest.opened || !action_stack_completed(&modal))
-		return;
-
-	switch (ev->type) {
-	case EVENT_CLICKDOWN:
-		if (maths_is_boxed(chest.x, chest.y, chest.sprite.cellw, chest.sprite.cellh,
-		    ev->click.x, ev->click.y)) {
-			chest.opened = 1;
-			message_action(&chest.msg, &chest.msg_act);
-			message_query(&chest.msg, NULL, &chest.msg.h);
-			action_stack_add(&modal, &chest.msg_act);
-		}
-	default:
-		break;
-	}
-}
-
-static void
-chest_draw(struct action *act)
-{
-	(void)act;
-
-	const int row = chest.opened ? chest.sprite.nrows - 1 : 0;
-	const int col = chest.opened ? chest.sprite.ncols - 1 : 0;
-
-	sprite_draw(&chest.sprite, row, col, chest.x, chest.y);
-}
-
-static void
-chest_init(void)
-{
-	if (image_open(&chest.image, PATH("sprites/chest.png")) < 0)
-		panic();
-
-	sprite_init(&chest.sprite, &chest.image, 32, 32);
-
-	chest.x = 100;
-	chest.y = 100;
-	chest.event.handle = chest_handle;
-	chest.event.draw = chest_draw;
-}
-
-static void
-init(void)
-{
-	if (core_init("fr.malikania", "actions") < 0 || ui_init() < 0 || rpg_init() < 0)
-		panic();
-	if (window_open("Example - Action", W, H) < 0)
-		panic();
-
-	guide_init();
-	chest_init();
-}
-
-static void
-handle(struct state *st, const union event *ev)
-{
-	(void)st;
-
-	switch (ev->type) {
-	case EVENT_QUIT:
-		game_quit();
-		break;
-	default:
-		action_stack_handle(&events, ev);
-		action_stack_handle(&modal, ev);
-		break;
-	}
-}
-
-static void
-update(struct state *st, unsigned int ticks)
-{
-	(void)st;
-
-	action_stack_update(&events, ticks);
-	action_stack_update(&modal, ticks);
-}
-
-static void
-draw(struct state *st)
-{
-	(void)st;
-
-	painter_set_color(0xffffffff);
-	painter_clear();
-	action_stack_draw(&events);
-	action_stack_draw(&modal);
-	painter_present();
-}
-
-static void
-run(void)
-{
-	struct state state = {
-		.handle = handle,
-		.update = update,
-		.draw = draw
-	};
-
-	action_stack_add(&events, &chest.event);
-	action_stack_add(&events, &guide.event);
-
-	game_push(&state);
-	game_loop();
-}
-
-static void
-quit(void)
-{
-	window_finish();
-	rpg_finish();
-	ui_finish();
-	core_finish();
-}
-
-int
-main(int argc, char **argv)
-{
-	(void)argc;
-	(void)argv;
-
-	init();
-	run();
-	quit();
-
-	return 0;
-}
--- a/src/examples/example-animation/main.c	Fri Oct 01 15:04:00 2021 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,145 +0,0 @@
-/*
- * example-animation.c -- example on how to use animations
- *
- * Copyright (c) 2020-2021 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <core/animation.h>
-#include <core/core.h>
-#include <core/event.h>
-#include <core/game.h>
-#include <core/image.h>
-#include <core/sys.h>
-#include <core/window.h>
-#include <core/painter.h>
-#include <core/panic.h>
-#include <core/sprite.h>
-#include <core/state.h>
-#include <core/texture.h>
-#include <core/util.h>
-
-#include <ui/label.h>
-#include <ui/ui.h>
-
-#define W       1280
-#define H       720
-#define PATH(r) util_pathf("%s/mlk-adventure/%s", sys_dir(SYS_DIR_DATA), r)
-
-static struct label label = {
-	.text = "Keys: <Space> start or reset the animation.",
-	.x = 10,
-	.y = 10,
-	.flags = LABEL_FLAGS_SHADOW
-};
-
-static struct texture numbers;
-static struct animation animation;
-static struct sprite sprite;
-static int completed = 1;
-
-static void
-init(void)
-{
-	if (core_init("fr.malikania", "animation") < 0 || ui_init() < 0)
-		panic();
-	if (window_open("Example - Animation", W, H) < 0)
-		panic();
-	if (image_open(&numbers, PATH("sprites/numbers.png")) < 0)
-		panic();
-}
-
-static void
-handle(struct state *st, const union event *ev)
-{
-	(void)st;
-
-	switch (ev->type) {
-	case EVENT_KEYDOWN:
-		switch (ev->key.key) {
-		case KEY_SPACE:
-			animation_start(&animation);
-			completed = animation_completed(&animation);
-			break;
-		default:
-			break;
-		}
-		break;
-	case EVENT_QUIT:
-		game_quit();
-		break;
-	default:
-		break;
-	}
-}
-
-static void
-update(struct state *st, unsigned int ticks)
-{
-	(void)st;
-
-	if (!completed)
-		completed = animation_update(&animation, ticks);
-}
-
-static void
-draw(struct state *st)
-{
-	(void)st;
-
-	painter_set_color(0x4f8fbaff);
-	painter_clear();
-	label_draw(&label);
-
-	if (!completed)
-		animation_draw(&animation, (window.w - sprite.cellw) / 2, (window.h - sprite.cellh) / 2);
-
-	painter_present();
-}
-
-static void
-run(void)
-{
-	struct state state = {
-		.handle = handle,
-		.update = update,
-		.draw = draw
-	};
-
-	sprite_init(&sprite, &numbers, 48, 48);
-	animation_init(&animation, &sprite, 1000);
-
-	game_push(&state);
-	game_loop();
-}
-
-static void
-quit(void)
-{
-	window_finish();
-	ui_finish();
-	core_finish();
-}
-
-int
-main(int argc, char **argv)
-{
-	(void)argc;
-	(void)argv;
-
-	init();
-	run();
-	quit();
-}
-
--- a/src/examples/example-audio/main.c	Fri Oct 01 15:04:00 2021 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,157 +0,0 @@
-/*
- * example-audio.c -- show how to use sounds and music
- *
- * Copyright (c) 2020-2021 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <core/core.h>
-#include <core/event.h>
-#include <core/game.h>
-#include <core/music.h>
-#include <core/painter.h>
-#include <core/panic.h>
-#include <core/sound.h>
-#include <core/state.h>
-#include <core/sys.h>
-#include <core/util.h>
-#include <core/window.h>
-
-#include <ui/label.h>
-#include <ui/theme.h>
-#include <ui/ui.h>
-
-#define W       1280
-#define H       720
-#define PATH(r) util_pathf("%s/mlk-adventure/%s", sys_dir(SYS_DIR_DATA), r)
-
-static struct music music;
-static struct sound sound;
-
-static struct label label_music = {
-	.text = "Music: <Space> play, <f> fade in, <s> fade out, <p> pause, <r> resume, <q> stop, <l> loop.",
-	.x = 10,
-	.y = 10,
-	.flags = LABEL_FLAGS_SHADOW
-};
-
-static struct label label_sound = {
-	.text = "Sound: click anywhere to pop a sound.",
-	.x = 10,
-	.y = 30,
-	.flags = LABEL_FLAGS_SHADOW
-};
-
-static void
-init(void)
-{
-	if (core_init("fr.malikania", "audio") < 0 || ui_init() < 0)
-		panic();
-	if (window_open("Example - Audio", W, H) < 0)
-		panic();
-	if (music_open(&music, PATH("music/vabsounds-romance.ogg")) < 0 ||
-	    sound_open(&sound, PATH("sounds/fire.wav")) < 0)
-		panic();
-}
-
-static void
-quit(void)
-{
-	window_finish();
-	ui_finish();
-	core_finish();
-}
-
-static void
-handle(struct state *st, const union event *ev)
-{
-	(void)st;
-
-	switch (ev->type) {
-	case EVENT_CLICKDOWN:
-		if (sound_play(&sound, -1, 0) < 0)
-			panic();
-		break;
-	case EVENT_KEYDOWN:
-		switch (ev->key.key) {
-		case KEY_f:
-			music_play(&music, 0, 500);
-			break;
-		case KEY_s:
-			music_stop(500);
-			break;
-		case KEY_p:
-			music_pause();
-			break;
-		case KEY_r:
-			music_resume();
-			break;
-		case KEY_q:
-			music_stop(0);
-			break;
-		case KEY_l:
-			music_play(&music, MUSIC_LOOP, 0);
-			break;
-		case KEY_SPACE:
-			music_play(&music, 0, 0);
-			break;
-		default:
-			break;
-		}
-		break;
-	case EVENT_QUIT:
-		game_quit();
-		break;
-	default:
-		break;
-	}
-}
-
-static void
-draw(struct state *st)
-{
-	(void)st;
-
-	painter_set_color(0x006554ff);
-	painter_clear();
-	label_draw(&label_music);
-	label_draw(&label_sound);
-	painter_present();
-}
-
-static void
-run(void)
-{
-	struct state state = {
-		.handle = handle,
-		.draw = draw
-	};
-
-	game_push(&state);
-	game_loop();
-
-	music_finish(&music);
-	sound_finish(&sound);
-}
-
-int
-main(int argc, char **argv)
-{
-	(void)argc;
-	(void)argv;
-
-	init();
-	run();
-	quit();
-}
--- a/src/examples/example-battle/character-john.c	Fri Oct 01 15:04:00 2021 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,48 +0,0 @@
-/*
- * character-john.c -- john character
- *
- * Copyright (c) 2020-2021 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <rpg/character.h>
-
-#include "character-john.h"
-#include "spell-fire.h"
-#include "registry.h"
-
-static void
-adventurer_reset(struct character *ch)
-{
-	/* TODO: this function should compute the attack thanks to the level. */
-	ch->hpmax = 120;
-	ch->mpmax = 50;
-	ch->atk = 50;
-	ch->def = 50;
-	ch->agt = 50;
-	ch->luck = 50;
-}
-
-const struct character character_john = {
-	.name = "John ",
-	.type = "Adventurer",
-	.level = 1,
-	.hp = 120,
-	.mp = 50,
-	.reset = adventurer_reset,
-	.sprite = &registry_sprites[REGISTRY_TEXTURE_JOHN],
-	.spells = {
-		&spell_fire
-	}
-};
--- a/src/examples/example-battle/character-john.h	Fri Oct 01 15:04:00 2021 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,24 +0,0 @@
-/*
- * character-john.h -- john character
- *
- * Copyright (c) 2020-2021 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef EXAMPLES_BATTLE_CHARACTER_JOHN_H
-#define EXAMPLES_BATTLE_CHARACTER_JOHN_H
-
-const struct character character_john;
-
-#endif /* !EXAMPLES_BATTLE_CHARACTER_JOHN_H */
--- a/src/examples/example-battle/main.c	Fri Oct 01 15:04:00 2021 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,295 +0,0 @@
-/*
- * example-battle.c -- show how to use battle
- *
- * Copyright (c) 2020-2021 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <assert.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <core/alloc.h>
-#include <core/core.h>
-#include <core/event.h>
-#include <core/game.h>
-#include <core/image.h>
-#include <core/painter.h>
-#include <core/panic.h>
-#include <core/sprite.h>
-#include <core/state.h>
-#include <core/sys.h>
-#include <core/texture.h>
-#include <core/util.h>
-#include <core/window.h>
-
-#include <ui/align.h>
-#include <ui/label.h>
-#include <ui/theme.h>
-#include <ui/ui.h>
-
-#include <rpg/character.h>
-#include <rpg/battle.h>
-#include <rpg/rpg.h>
-#include <rpg/spell.h>
-
-#include "registry.h"
-#include "spell-fire.h"
-
-#define W 1280
-#define H 720
-
-static void
-adventurer_reset(struct character *ch)
-{
-	/* TODO: this function should compute the attack thanks to the level. */
-	ch->hpmax = 120;
-	ch->mpmax = 50;
-	ch->atk = 50;
-	ch->def = 50;
-	ch->agt = 50;
-	ch->luck = 50;
-}
-
-static void
-haunted_wood_reset(struct character *ch)
-{
-	ch->hpmax = ch->hp = 2000;
-	ch->mpmax = ch->mp = 250;
-	ch->atk = 178;
-	ch->def = 80;
-	ch->agt = 80;
-	ch->luck = 100;
-}
-
-static void
-black_cat_reset(struct character *ch)
-{
-	ch->hpmax = ch->hp = 126;
-	ch->mpmax = ch->mp = 38;
-	ch->atk = 22;
-	ch->def = 19;
-	ch->agt = 11;
-	ch->luck = 14;
-}
-
-static struct character team[] = {
-	{
-		.name = "Molko",
-		.level = 1,
-		.hp = 120,
-		.mp = 50,
-		.reset = adventurer_reset,
-		.sprites = {
-			[CHARACTER_SPRITE_NORMAL] = &registry_sprites[REGISTRY_TEXTURE_JOHN_WALK],
-			[CHARACTER_SPRITE_SWORD] = &registry_sprites[REGISTRY_TEXTURE_JOHN_SWORD],
-		},
-		.spells = {
-			&spell_fire
-		}
-	},
-};
-
-static void
-haunted_wood_strat(struct character *ch, struct battle *bt)
-{
-	(void)ch;
-
-	/* TODO: Select randomly. */
-	battle_attack(bt, bt->order_cur->ch, bt->team[0].ch);
-}
-
-static void
-black_cat_strat(struct character *ch, struct battle *bt)
-{
-	(void)ch;
-
-	/* TODO: Select randomly. */
-	battle_attack(bt, bt->order_cur->ch, bt->team[0].ch);
-}
-
-static struct character haunted_wood = {
-	.name = "Haunted Wood",
-	.level = 30,
-	.reset = haunted_wood_reset,
-	.sprites = {
-		[CHARACTER_SPRITE_NORMAL] = &registry_sprites[REGISTRY_TEXTURE_HAUNTED_WOOD],
-	},
-	.exec = haunted_wood_strat
-};
-
-static struct character black_cat = {
-	.name = "Black Cat",
-	.level = 6,
-	.reset = black_cat_reset,
-	.sprites = {
-		[CHARACTER_SPRITE_NORMAL] = &registry_sprites[REGISTRY_TEXTURE_BLACK_CAT],
-	},
-	.exec = black_cat_strat
-};
-
-static void
-init(void)
-{
-	if (core_init("fr.malikania", "battle") < 0 || ui_init() < 0 || rpg_init() < 0)
-		panic();
-	if (window_open("Example - Battle", W, H) < 0)
-		panic();
-
-	registry_init();
-
-	/* Set cursor in default theme. */
-	theme_default()->sprites[THEME_SPRITE_CURSOR] = &registry_sprites[REGISTRY_TEXTURE_CURSOR];
-}
-
-static struct state fight_state;
-
-static void
-prepare_to_fight(void)
-{
-	struct battle *bt = alloc_new0(sizeof (*bt));
-
-//	bt->enemies[0].ch = &haunted_wood;
-	bt->team[0].ch = &team[0];
-	//bt->team[1].ch = &team[1];
-
-	/* Positionate the single ennemy to the left. */
-	align(ALIGN_LEFT,
-	    &bt->enemies[0].x, &bt->enemies[0].y,
-	    haunted_wood.sprites[CHARACTER_SPRITE_NORMAL]->cellw,
-	    haunted_wood.sprites[CHARACTER_SPRITE_NORMAL]->cellh,
-	    0, 0, window.w, window.h);
-
-	/* Black cat is near the previous monster. */
-	bt->enemies[1].ch = &black_cat;
-	bt->enemies[1].x = 500;
-	bt->enemies[1].y = 100;
-
-	bt->background = &registry_images[REGISTRY_IMAGE_BATTLE_BACKGROUND];
-
-	battle_start(bt);
-
-	fight_state.data = bt;
-	game_push(&fight_state);
-}
-
-
-static void
-empty_handle(struct state *st, const union event *ev)
-{
-	(void)st;
-
-	switch (ev->type) {
-	case EVENT_QUIT:
-		game_quit();
-		break;
-	case EVENT_KEYDOWN:
-		if (ev->key.key == KEY_SPACE)
-			prepare_to_fight();
-		break;
-	default:
-		break;
-	}
-}
-
-static void
-empty_draw(struct state *st)
-{
-	(void)st;
-
-	static const struct label info = {
-		.text = "Press <Space> to start a battle.",
-		.x = 10,
-		.y = 10,
-		.flags = LABEL_FLAGS_SHADOW
-	};
-
-	painter_set_color(0x4f8fbaff);
-	painter_clear();
-	label_draw(&info);
-	painter_present();
-}
-
-static struct state empty_state = {
-	.handle = empty_handle,
-	.draw = empty_draw
-};
-
-static void
-fight_handle(struct state *st, const union event *ev)
-{
-	battle_handle(st->data, ev);
-}
-
-static void
-fight_update(struct state *st, unsigned int ticks)
-{
-	struct battle *bt = st->data;
-
-	if (battle_update(bt, ticks))
-		game_push(&empty_state);
-}
-
-static void
-fight_draw(struct state *st)
-{
-	painter_set_color(0x000000ff);
-	painter_clear();
-	battle_draw(st->data);
-	painter_present();
-}
-
-static void
-fight_finish(struct state *st)
-{
-	battle_finish(st->data);
-	free(st->data);
-}
-
-static struct state fight_state = {
-	.handle = fight_handle,
-	.update = fight_update,
-	.draw = fight_draw,
-	.finish = fight_finish,
-};
-
-static void
-run(void)
-{
-	game_push(&empty_state);
-	game_loop();
-}
-
-static void
-quit(void)
-{
-	registry_finish();
-	theme_finish();
-	window_finish();
-	sys_finish();
-}
-
-int
-main(int argc, char **argv)
-{
-	--argc;
-	++argv;
-
-	init();
-	run();
-	quit();
-
-	return 0;
-}
--- a/src/examples/example-battle/registry.c	Fri Oct 01 15:04:00 2021 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,128 +0,0 @@
-/*
- * registry.h -- registry of resources
- *
- * Copyright (c) 2020-2021 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <stddef.h>
-
-#include <core/image.h>
-#include <core/panic.h>
-#include <core/util.h>
-#include <core/sys.h>
-
-#include "registry.h"
-
-#define PATH(r) util_pathf("%s/mlk-adventure/%s", sys_dir(SYS_DIR_DATA), r)
-
-struct texture registry_images[REGISTRY_IMAGE_NUM];
-struct texture registry_textures[REGISTRY_TEXTURE_NUM];
-struct sprite registry_sprites[REGISTRY_TEXTURE_NUM];
-struct sound registry_sounds[REGISTRY_SOUND_NUM];
-
-#define REGISTRY_IMAGE(i, path) \
-	{ (i), (path) }
-
-static const struct {
-	enum registry_image index;
-	const char *path;
-} images[] = {
-	REGISTRY_IMAGE(REGISTRY_IMAGE_BATTLE_BACKGROUND, "images/battle-background.png")
-};
-
-#define REGISTRY_TEXTURE(s, path, cw, ch) \
-	{ (s), (path), (cw), (ch) }
-
-static const struct {
-	enum registry_texture index;
-	const char *path;
-	unsigned int cellw;
-	unsigned int cellh;
-} textures[] = {
-	REGISTRY_TEXTURE(REGISTRY_TEXTURE_CURSOR, "sprites/ui-cursor.png", 24, 24),
-	REGISTRY_TEXTURE(REGISTRY_TEXTURE_EXPLOSION, "sprites/explosion.png", 256, 256),
-	REGISTRY_TEXTURE(REGISTRY_TEXTURE_JOHN_SWORD, "sprites/john-sword.png", 256, 256),
-	REGISTRY_TEXTURE(REGISTRY_TEXTURE_JOHN_WALK, "sprites/john-walk.png", 256, 256),
-	REGISTRY_TEXTURE(REGISTRY_TEXTURE_HAUNTED_WOOD, "images/haunted-wood.png", 0, 0),
-	REGISTRY_TEXTURE(REGISTRY_TEXTURE_BLACK_CAT, "images/black-cat.png", 0, 0)
-};
-
-#define REGISTRY_SOUND(s, path) \
-	{ (s), (path) }
-
-static const struct {
-	enum registry_sound index;
-	const char *path;
-} sounds[] = {
-	REGISTRY_SOUND(REGISTRY_SOUND_FIRE, "sounds/fire.wav")
-};
-
-static void
-load_images(void)
-{
-	for (size_t i = 0; i < UTIL_SIZE(images); ++i) {
-		struct texture *texture = &registry_images[images[i].index];
-
-		if (image_open(texture, PATH(images[i].path)) < 0)
-			panic();
-	}
-}
-
-static void
-load_textures_and_sprites(void)
-{
-	for (size_t i = 0; i < UTIL_SIZE(textures); ++i) {
-		struct texture *texture = &registry_textures[textures[i].index];
-		struct sprite *sprite = &registry_sprites[textures[i].index];
-
-		if (image_open(texture, PATH(textures[i].path)) < 0)
-			panic();
-
-		if (textures[i].cellw == 0 || textures[i].cellh == 0)
-			sprite_init(sprite, texture, texture->w, texture->h);
-		else
-			sprite_init(sprite, texture, textures[i].cellw, textures[i].cellh);
-	}
-}
-
-static void
-load_sounds(void)
-{
-	for (size_t i = 0; i < UTIL_SIZE(sounds); ++i) {
-		struct sound *sound = &registry_sounds[sounds[i].index];
-
-		if (sound_open(sound, PATH(sounds[i].path)) < 0)
-			panic();
-	}
-}
-
-void
-registry_init(void)
-{
-	load_images();
-	load_textures_and_sprites();
-	load_sounds();
-}
-
-void
-registry_finish(void)
-{
-	for (size_t i = 0; i < UTIL_SIZE(registry_images); ++i)
-		texture_finish(&registry_images[i]);
-	for (size_t i = 0; i < UTIL_SIZE(registry_textures); ++i)
-		texture_finish(&registry_textures[i]);
-	for (size_t i = 0; i < UTIL_SIZE(registry_sounds); ++i)
-		sound_finish(&registry_sounds[i]);
-}
--- a/src/examples/example-battle/registry.h	Fri Oct 01 15:04:00 2021 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,66 +0,0 @@
-/*
- * registry.h -- registry of resources
- *
- * Copyright (c) 2020-2021 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef EXAMPLES_BATTLE_REGISTRY_H
-#define EXAMPLES_BATTLE_REGISTRY_H
-
-#include <core/sound.h>
-#include <core/sprite.h>
-#include <core/texture.h>
-
-enum registry_texture {
-	/* UI. */
-	REGISTRY_TEXTURE_CURSOR,
-
-	/* Animations. */
-	REGISTRY_TEXTURE_EXPLOSION,
-
-	/* Characters. */
-	REGISTRY_TEXTURE_JOHN_WALK,
-	REGISTRY_TEXTURE_JOHN_SWORD,
-
-	/* Enemies. */
-	REGISTRY_TEXTURE_HAUNTED_WOOD,
-	REGISTRY_TEXTURE_BLACK_CAT,
-
-	/* Unused.*/
-	REGISTRY_TEXTURE_NUM
-};
-
-enum registry_image {
-	REGISTRY_IMAGE_BATTLE_BACKGROUND,
-	REGISTRY_IMAGE_NUM
-};
-
-enum registry_sound {
-	REGISTRY_SOUND_FIRE,
-	REGISTRY_SOUND_NUM
-};
-
-extern struct texture registry_images[REGISTRY_IMAGE_NUM];
-extern struct texture registry_textures[REGISTRY_TEXTURE_NUM];
-extern struct sprite registry_sprites[REGISTRY_TEXTURE_NUM];
-extern struct sound registry_sounds[REGISTRY_SOUND_NUM];
-
-void
-registry_init(void);
-
-void
-registry_finish(void);
-
-#endif /* !EXAMPLES_BATTLE_REGISTRY_H */
--- a/src/examples/example-battle/spell-fire.c	Fri Oct 01 15:04:00 2021 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,129 +0,0 @@
-/*
- * spell-fire.c -- example of spell: fire
- *
- * Copyright (c) 2020-2021 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <stdlib.h>
-
-#include <core/action.h>
-#include <core/animation.h>
-#include <core/alloc.h>
-
-#include <ui/align.h>
-
-#include <rpg/battle.h>
-#include <rpg/character.h>
-#include <rpg/spell.h>
-
-#include "registry.h"
-#include "spell-fire.h"
-
-struct data {
-	struct battle *battle;
-	struct character *target;
-	struct animation animation;
-	struct action action;
-	unsigned int selection;
-};
-
-static int
-update(struct action *act, unsigned int ticks)
-{
-	struct data *data = act->data;
-
-	return animation_update(&data->animation, ticks);
-}
-
-static void
-draw(struct action *act)
-{
-	const struct data *data = act->data;
-	const struct battle_entity *et = &data->battle->enemies[data->selection];
-	const struct sprite *sprite = et->ch->sprites[CHARACTER_SPRITE_NORMAL];
-	int x, y;
-
-	align(ALIGN_CENTER,
-	    &x, &y, data->animation.sprite->cellw, data->animation.sprite->cellh,
-	    et->x, et->y, sprite->cellw, sprite->cellh);
-
-	animation_draw(&data->animation, x, y);
-}
-
-static void
-end(struct action *act)
-{
-	struct data *data = act->data;
-	struct character *ch = data->battle->enemies[data->selection].ch;
-
-	/* TODO: compute damage. */
-	const unsigned int damage = 100;
-
-	if ((unsigned int)ch->hp < damage)
-		ch->hp = 0;
-	else
-		ch->hp -= damage;
-
-	battle_indicator_hp(data->battle, data->battle->enemies[data->selection].ch, 100);
-}
-
-static void
-finish(struct action *act)
-{
-	free(act->data);
-}
-
-static void
-fire_select(const struct battle *bt, struct selection *slt)
-{
-	slt->index_side = 0;
-
-	selection_first(slt, bt);
-}
-
-static void
-fire_action(struct battle *bt, struct character *owner, const struct selection *slt)
-{
-	struct data *data;
-
-	(void)owner;
-
-	data = alloc_new0(sizeof (*data));
-	data->selection = slt->index_character;
-	data->battle = bt;
-	data->action.data = data;
-	data->action.update = update;
-	data->action.draw = draw;
-	data->action.finish = finish;
-	data->action.end = end;
-
-	animation_init(&data->animation, &registry_sprites[REGISTRY_TEXTURE_EXPLOSION], 12);
-	animation_start(&data->animation);
-
-	sound_play(&registry_sounds[REGISTRY_SOUND_FIRE], -1, 0);
-
-	action_stack_add(&bt->actions[0], &data->action);
-}
-
-const struct spell spell_fire = {
-	.name = "Fire",
-	.description = "A delicate fire.",
-	.mp = 5,
-	.type = SPELL_TYPE_FIRE,
-	.select = fire_select,
-	.select_kind = SELECTION_KIND_ONE,
-	.select_side = SELECTION_SIDE_ENEMY,
-	.action = fire_action
-};
--- a/src/examples/example-battle/spell-fire.h	Fri Oct 01 15:04:00 2021 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,24 +0,0 @@
-/*
- * spell-fire.c -- example of spell: fire
- *
- * Copyright (c) 2020-2021 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef EXAMPLES_BATTLE_SPELL_FIRE_H
-#define EXAMPLES_BATTLE_SPELL_FIRE_H
-
-extern const struct spell spell_fire;
-
-#endif /* !EXAMPLES_BATTLE_SPELL_FIRE_H */
--- a/src/examples/example-cursor/main.c	Fri Oct 01 15:04:00 2021 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,146 +0,0 @@
-/*
- * example-cursor.c -- example on how to change cursor
- *
- * Copyright (c) 2020-2021 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <stdio.h>
-
-#include <core/core.h>
-#include <core/event.h>
-#include <core/game.h>
-#include <core/key.h>
-#include <core/painter.h>
-#include <core/panic.h>
-#include <core/state.h>
-#include <core/sys.h>
-#include <core/util.h>
-#include <core/window.h>
-
-#include <ui/label.h>
-#include <ui/ui.h>
-
-#define W 1280
-#define H 720
-
-static char help_text[128];
-static enum window_cursor cursor = WINDOW_CURSOR_ARROW;
-
-static struct label help = {
-	.x = 10,
-	.y = 10,
-	.text = help_text,
-	.flags = LABEL_FLAGS_SHADOW
-};
-
-static void
-init(void)
-{
-	if (core_init("fr.malikania", "cursor") < 0 || ui_init() < 0)
-		panic();
-	if (window_open("Example - Cursor", W, H) < 0)
-		panic();
-}
-
-static void
-change(enum window_cursor cursor)
-{
-	static const char *names[] = {
-		[WINDOW_CURSOR_ARROW]           = "WINDOW_CURSOR_ARROW",
-		[WINDOW_CURSOR_EDIT]            = "WINDOW_CURSOR_EDIT",
-		[WINDOW_CURSOR_WAIT]            = "WINDOW_CURSOR_WAIT",
-		[WINDOW_CURSOR_CROSSHAIR]       = "WINDOW_CURSOR_CROSSHAIR",
-		[WINDOW_CURSOR_SIZE]            = "WINDOW_CURSOR_SIZE",
-		[WINDOW_CURSOR_NO]              = "WINDOW_CURSOR_NO",
-		[WINDOW_CURSOR_HAND]            = "WINDOW_CURSOR_HAND"
-	};
-
-	snprintf(help_text, sizeof (help_text), "Keys: <Left>/<Right> to change cursor. Current: %s", names[cursor]);
-	window_set_cursor(cursor);
-}
-
-static void
-handle(struct state *st, const union event *ev)
-{
-	(void)st;
-
-	switch (ev->type) {
-	case EVENT_KEYDOWN:
-		switch (ev->key.key) {
-		case KEY_LEFT:
-			if (cursor > 0)
-				change(--cursor);
-			break;
-		case KEY_RIGHT:
-			if (cursor + 1 < WINDOW_CURSOR_LAST)
-				change(++cursor);
-			break;
-		default:
-			break;
-		}
-
-
-		break;
-	case EVENT_QUIT:
-		game_quit();
-		break;
-	default:
-		break;
-	}
-}
-
-static void
-draw(struct state *st)
-{
-	(void)st;
-
-	painter_set_color(0xebede9ff);
-	painter_clear();
-	label_draw(&help);
-	painter_present();
-}
-
-static void
-run(void)
-{
-	struct state state = {
-		.handle = handle,
-		.draw = draw
-	};
-
-	change(cursor);
-
-	game_push(&state);
-	game_loop();
-}
-
-static void
-quit(void)
-{
-	window_finish();
-	ui_finish();
-	core_finish();
-}
-
-int
-main(int argc, char **argv)
-{
-	(void)argc;
-	(void)argv;
-
-	init();
-	run();
-	quit();
-}
--- a/src/examples/example-debug/main.c	Fri Oct 01 15:04:00 2021 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,111 +0,0 @@
-/*
- * example-debug.c -- example on how to use debug interface
- *
- * Copyright (c) 2020-2021 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <core/core.h>
-#include <core/event.h>
-#include <core/game.h>
-#include <core/window.h>
-#include <core/painter.h>
-#include <core/panic.h>
-#include <core/state.h>
-#include <core/util.h>
-
-#include <ui/debug.h>
-#include <ui/theme.h>
-#include <ui/ui.h>
-
-#define W 1280
-#define H 720
-
-static int mouse_x;
-static int mouse_y;
-
-static void
-init(void)
-{
-	if (core_init("fr.malikania", "debug") < 0 || ui_init() < 0)
-		panic();
-	if (window_open("Example - Debug", W, H) < 0)
-		panic();
-
-	debug_options.enable = 1;
-}
-
-static void
-handle(struct state *st, const union event *ev)
-{
-	(void)st;
-
-	switch (ev->type) {
-	case EVENT_MOUSE:
-		mouse_x = ev->mouse.x;
-		mouse_y = ev->mouse.y;
-		break;
-	case EVENT_QUIT:
-		game_quit();
-		break;
-	default:
-		break;
-	}
-}
-
-static void
-draw(struct state *st)
-{
-	(void)st;
-
-	struct debug_report report = {0};
-
-	painter_set_color(0x4f8fbaff);
-	painter_clear();
-	debugf(&report, "Game running.");
-	debugf(&report, "mouse: %d, %d", mouse_x, mouse_y);
-	painter_present();
-}
-
-static void
-run(void)
-{
-	struct state state = {
-		.handle = handle,
-		.draw = draw
-	};
-
-	game_push(&state);
-	game_loop();
-}
-
-static void
-quit(void)
-{
-	window_finish();
-	ui_finish();
-	core_finish();
-}
-
-int
-main(int argc, char **argv)
-{
-	(void)argc;
-	(void)argv;
-
-	init();
-	run();
-	quit();
-}
-
--- a/src/examples/example-drawable/main.c	Fri Oct 01 15:04:00 2021 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,201 +0,0 @@
-/*
- * example-drawable.c -- example on how to use automatic drawables
- *
- * Copyright (c) 2020-2021 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-
-#include <core/alloc.h>
-#include <core/animation.h>
-#include <core/core.h>
-#include <core/event.h>
-#include <core/game.h>
-#include <core/drawable.h>
-#include <core/key.h>
-#include <core/painter.h>
-#include <core/panic.h>
-#include <core/sys.h>
-#include <core/image.h>
-#include <core/sprite.h>
-#include <core/state.h>
-#include <core/texture.h>
-#include <core/util.h>
-#include <core/window.h>
-
-#include <ui/label.h>
-#include <ui/theme.h>
-#include <ui/ui.h>
-
-#define W       1280
-#define H       720
-#define PATH(r) util_pathf("%s/mlk-adventure/%s", sys_dir(SYS_DIR_DATA), r)
-
-static struct label help = {
-	.x = 10,
-	.y = 10,
-	.text = "Keys: <Esc> to reset. Click anywhere to spawn a drawable.",
-	.flags = LABEL_FLAGS_SHADOW
-};
-
-static struct drawable_stack stack;
-
-/*
- * List of drawables for this example.
- * -----------------------------------------------------------------------------
- */
-
-/* 0: Explosion animation. */
-static struct texture explosion_tex;
-static struct sprite explosion_sprite;
-
-struct explosion {
-	struct animation anim;
-	struct drawable dw;
-};
-
-static void
-init(void)
-{
-	if (core_init("fr.malikania", "drawable") < 0 || ui_init() < 0)
-		panic();
-	if (window_open("Example - Drawable", W, H) < 0)
-		panic();
-
-	/* 0: Explosion animation. */
-	if (image_open(&explosion_tex, PATH("sprites/explosion.png")) < 0)
-		panic();
-
-	sprite_init(&explosion_sprite, &explosion_tex, 256, 256);
-}
-
-static int
-explosion_update(struct drawable *dw, unsigned int ticks)
-{
-	struct explosion *ex = dw->data;
-
-	return animation_update(&ex->anim, ticks);
-}
-
-static void
-explosion_draw(struct drawable *dw)
-{
-	struct explosion *ex = dw->data;
-
-	animation_draw(&ex->anim, ex->dw.x, ex->dw.y);
-}
-
-static void
-explosion_finish(struct drawable *dw)
-{
-	free(dw->data);
-}
-
-static void
-spawn(int x, int y)
-{
-	struct explosion *ex = alloc_new0(sizeof (*ex));
-
-	animation_init(&ex->anim, &explosion_sprite, 15);
-
-	ex->dw.data = ex;
-	ex->dw.x = x - (int)(explosion_sprite.cellw / 2);
-	ex->dw.y = y - (int)(explosion_sprite.cellh / 2);
-	ex->dw.update = explosion_update;
-	ex->dw.draw = explosion_draw;
-	ex->dw.finish = explosion_finish;
-
-	drawable_stack_add(&stack, &ex->dw);
-}
-
-static void
-handle(struct state *st, const union event *ev)
-{
-	(void)st;
-
-	switch (ev->type) {
-	case EVENT_KEYDOWN:
-		switch (ev->key.key) {
-		case KEY_ESCAPE:
-			drawable_stack_finish(&stack);
-			break;
-		default:
-			break;
-		}
-		break;
-	case EVENT_CLICKDOWN:
-		spawn(ev->click.x, ev->click.y);
-		break;
-	case EVENT_QUIT:
-		game_quit();
-		break;
-	default:
-		break;
-	}
-}
-
-static void
-update(struct state *st, unsigned int ticks)
-{
-	(void)st;
-
-	drawable_stack_update(&stack, ticks);
-
-}
-
-static void
-draw(struct state *st)
-{
-	(void)st;
-
-	painter_set_color(0xebede9ff);
-	painter_clear();
-	label_draw(&help);
-	drawable_stack_draw(&stack);
-	painter_present();
-}
-
-static void
-run(void)
-{
-	struct state state = {
-		.handle = handle,
-		.update = update,
-		.draw = draw
-	};
-
-	game_push(&state);
-	game_loop();
-}
-
-static void
-quit(void)
-{
-	window_finish();
-	ui_finish();
-	core_finish();
-}
-
-int
-main(int argc, char **argv)
-{
-	(void)argc;
-	(void)argv;
-
-	init();
-	run();
-	quit();
-}
--- a/src/examples/example-font/main.c	Fri Oct 01 15:04:00 2021 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,146 +0,0 @@
-/*
- * example-font.c -- show how to use fonts
- *
- * Copyright (c) 2020-2021 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <core/core.h>
-#include <core/event.h>
-#include <core/font.h>
-#include <core/game.h>
-#include <core/painter.h>
-#include <core/panic.h>
-#include <core/state.h>
-#include <core/sys.h>
-#include <core/texture.h>
-#include <core/util.h>
-#include <core/window.h>
-
-#include <ui/theme.h>
-#include <ui/ui.h>
-
-#define W       (1280)
-#define H       (720)
-
-/* Friendly taken from: https://lospec.com/palette-list/apollo */
-static const unsigned long colors[] = {
-	0x3c5e8bff,     /* Blue. */
-	0x468232ff,     /* Green. */
-	0xad7757ff,     /* Light brown. */
-	0x884b2bff,     /* Brown. */
-	0x752438ff,     /* Red. */
-	0x7a367bff,     /* Magenta. */
-	0x151d28ff,     /* Dark */
-	0xc7cfccff,     /* Christian Grey. */
-};
-
-static int ci = 0;
-static enum font_style style = FONT_STYLE_ANTIALIASED;
-
-static void
-init(void)
-{
-	if (core_init("fr.malikania", "font") < 0 || ui_init() < 0)
-		panic();
-	if (window_open("Example - Font", W, H) < 0)
-		panic();
-}
-
-static void
-handle(struct state *st, const union event *ev)
-{
-	(void)st;
-
-	switch (ev->type) {
-	case EVENT_KEYDOWN:
-		switch (ev->key.key) {
-		case KEY_LEFT:
-			if (ci > 0)
-				ci--;
-			break;
-		case KEY_RIGHT:
-			if ((size_t)ci < UTIL_SIZE(colors))
-				ci++;
-			break;
-		case KEY_SPACE:
-			if (style == FONT_STYLE_ANTIALIASED)
-				style = FONT_STYLE_NONE;
-			else
-				style = FONT_STYLE_ANTIALIASED;
-
-			theme_default()->fonts[THEME_FONT_INTERFACE]->style = style;
-		default:
-			break;
-		}
-		break;
-	case EVENT_QUIT:
-		game_quit();
-		break;
-	default:
-		break;
-	}
-}
-
-static void
-draw(struct state *st)
-{
-	(void)st;
-
-	struct font *font = theme_default()->fonts[THEME_FONT_INTERFACE];
-	struct texture tex;
-
-	painter_set_color(0xffffffff);
-	painter_clear();
-
-	if (font_render(font, &tex, "Example of text. Use <Left>/<Right> to change color and <Space> to toggle antialiasing.", colors[ci]) < 0)
-		panic();
-
-	texture_draw(&tex, 10, 10);
-	painter_present();
-	texture_finish(&tex);
-}
-
-static void
-run(void)
-{
-	struct state state = {
-		.handle = handle,
-		.draw = draw
-	};
-
-	game_push(&state);
-	game_loop();
-}
-
-static void
-quit(void)
-{
-	window_finish();
-	ui_finish();
-	core_finish();
-}
-
-int
-main(int argc, char **argv)
-{
-	(void)argc;
-	(void)argv;
-
-	init();
-	run();
-	quit();
-
-	return 0;
-}
--- a/src/examples/example-gridmenu/main.c	Fri Oct 01 15:04:00 2021 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,139 +0,0 @@
-/*
- * example-gridmenu.c -- show how to use grid menu
- *
- * Copyright (c) 2020-2021 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <core/core.h>
-#include <core/event.h>
-#include <core/game.h>
-#include <core/painter.h>
-#include <core/panic.h>
-#include <core/state.h>
-#include <core/sys.h>
-#include <core/trace.h>
-#include <core/util.h>
-#include <core/window.h>
-
-#include <ui/align.h>
-#include <ui/gridmenu.h>
-#include <ui/label.h>
-#include <ui/theme.h>
-#include <ui/ui.h>
-
-#define W       (1280)
-#define H       (720)
-
-static void
-init(void)
-{
-	if (core_init("fr.malikania", "grid-menu") < 0 || ui_init() < 0)
-		panic();
-	if (window_open("Example - Grid menu", W, H) < 0)
-		panic();
-}
-
-static void
-quit(void)
-{
-	window_finish();
-	ui_finish();
-	core_finish();
-}
-
-static void
-handle(struct state *st, const union event *ev)
-{
-	switch (ev->type) {
-	case EVENT_QUIT:
-		game_quit();
-		break;
-	default:
-		gridmenu_handle(st->data, ev);
-		break;
-	}
-}
-
-static void
-update(struct state *st, unsigned int ticks)
-{
-	(void)ticks;
-
-	struct gridmenu *menu = st->data;
-
-	if (menu->state == GRIDMENU_STATE_ACTIVATED) {
-		tracef("selected index: %u", (unsigned int)menu->selected);
-		gridmenu_reset(menu);
-	}
-}
-
-static void
-draw(struct state *st)
-{
-	painter_set_color(0x4f8fbaff);
-	painter_clear();
-	gridmenu_draw(st->data);
-	painter_present();
-}
-
-static void
-run(void)
-{
-	struct gridmenu menu = {
-		.menu = {
-			"Feu mineur",
-			"Feu majeur",
-			"Feu septième",
-			"Glace mineure",
-			"Glace majeure",
-			"Glace septième",
-			"Foudre mineure",
-			"Foudre majeure",
-			"Foudre septième",
-			"Choc mineur",
-			"Choc majeur",
-			"Choc septième",
-		},
-		.w = 300,
-		.h = 100,
-		.nrows = 3,
-		.ncols = 2
-	};
-	struct state state = {
-		.data = &menu,
-		.handle = handle,
-		.update = update,
-		.draw = draw,
-	};
-
-	align(ALIGN_CENTER, &menu.x, &menu.y, menu.w, menu.h, 0, 0, W, H);
-
-	/* Need to repaint at least once. */
-	gridmenu_repaint(&menu);
-
-	game_push(&state);
-	game_loop();
-}
-
-int
-main(int argc, char **argv)
-{
-	(void)argc;
-	(void)argv;
-
-	init();
-	run();
-	quit();
-}
--- a/src/examples/example-label/main.c	Fri Oct 01 15:04:00 2021 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,183 +0,0 @@
-/*
- * example-label.c -- show how to use label and alignments
- *
- * Copyright (c) 2020-2021 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <stddef.h>
-
-#include <core/core.h>
-#include <core/event.h>
-#include <core/game.h>
-#include <core/painter.h>
-#include <core/panic.h>
-#include <core/state.h>
-#include <core/sys.h>
-#include <core/util.h>
-#include <core/window.h>
-
-#include <ui/align.h>
-#include <ui/label.h>
-#include <ui/theme.h>
-#include <ui/ui.h>
-
-#define W       (1280)
-#define H       (720)
-
-struct {
-	enum align align;
-	struct label label;
-} table[] = {
-	{
-		.align = ALIGN_TOP_LEFT,
-		.label = {
-			.text = "Top left"
-		}
-	},
-	{
-		.align = ALIGN_TOP,
-		.label = {
-			.text = "Top",
-		}
-	},
-	{
-		.align = ALIGN_TOP_RIGHT,
-		.label = {
-			.text = "Top right",
-		}
-	},
-	{
-		.align = ALIGN_RIGHT,
-		.label = {
-			.text = "Right",
-		}
-	},
-	{
-		.align = ALIGN_BOTTOM_RIGHT,
-		.label = {
-			.text = "Bottom right",
-		}
-	},
-	{
-		.align = ALIGN_BOTTOM,
-		.label = {
-			.text = "Bottom",
-		}
-	},
-	{
-		.align = ALIGN_BOTTOM_LEFT,
-		.label = {
-			.text = "Bottom left",
-		}
-	},
-	{
-		.align = ALIGN_LEFT,
-		.label = {
-			.text = "Left",
-		}
-	},
-	{
-		.align = ALIGN_CENTER,
-		.label = {
-			.text = "The world is Malikania.",
-			.flags = LABEL_FLAGS_SHADOW
-		}
-	}
-};
-
-static struct label mlabel = {
-	.text = "This one follows your mouse and is not aligned."
-};
-
-static void
-init(void)
-{
-	if (core_init("fr.malikania", "label") < 0 || ui_init() < 0)
-		panic();
-	if (window_open("Example - Label", W, H) < 0)
-		panic();
-
-	for (size_t i = 0; i < UTIL_SIZE(table); ++i) {
-		struct label *l = &table[i].label;
-		unsigned int w, h;
-
-		label_query(l, &w, &h);
-		align(table[i].align, &l->x, &l->y, w, h, 0, 0, W, H);
-	}
-}
-
-static void
-quit(void)
-{
-	window_finish();
-	ui_finish();
-	core_finish();
-}
-
-static void
-handle(struct state *st, const union event *ev)
-{
-	(void)st;
-
-	switch (ev->type) {
-	case EVENT_MOUSE:
-		mlabel.x = ev->mouse.x;
-		mlabel.y = ev->mouse.y;
-		break;
-	case EVENT_QUIT:
-		game_quit();
-		break;
-	default:
-		break;
-	}
-}
-
-static void
-draw(struct state *st)
-{
-	(void)st;
-
-	painter_set_color(0x4f8fbaff);
-	painter_clear();
-
-	for (size_t i = 0; i < UTIL_SIZE(table); ++i)
-		label_draw(&table[i].label);
-
-	label_draw(&mlabel);
-	painter_present();
-}
-
-static void
-run(void)
-{
-	struct state state = {
-		.handle = handle,
-		.draw = draw
-	};
-
-	game_push(&state);
-	game_loop();
-}
-
-int
-main(int argc, char **argv)
-{
-	(void)argc;
-	(void)argv;
-
-	init();
-	run();
-	quit();
-}
--- a/src/examples/example-message/main.c	Fri Oct 01 15:04:00 2021 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,327 +0,0 @@
-/*
- * example-message.c -- show how to use messages
- *
- * Copyright (c) 2020-2021 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <stddef.h>
-
-#include <core/core.h>
-#include <core/event.h>
-#include <core/game.h>
-#include <core/painter.h>
-#include <core/panic.h>
-#include <core/state.h>
-#include <core/sys.h>
-#include <core/util.h>
-#include <core/window.h>
-
-#include <ui/frame.h>
-#include <ui/theme.h>
-#include <ui/ui.h>
-
-#include <rpg/message.h>
-#include <rpg/rpg.h>
-
-#define W       (1280)
-#define H       (720)
-
-#define MW      (W * 0.75)
-#define MH      (H * 0.120)
-#define MX      ((W / 2) - (MW / 2))
-#define MY      (100)
-
-static void
-init(void)
-{
-	if (core_init("fr.malikania", "message") < 0 || ui_init() < 0 || rpg_init() < 0)
-		panic();
-	if (window_open("Example - Message", W, H) < 0)
-		panic();
-}
-
-static void
-quit(void)
-{
-	window_finish();
-	rpg_finish();
-	ui_finish();
-	core_finish();
-}
-
-static void
-handle(struct state *st, const union event *ev)
-{
-	switch (ev->type) {
-	case EVENT_QUIT:
-		game_quit();
-		break;
-	default:
-		message_handle(st->data, ev);
-		break;
-	}
-}
-
-static void
-update(struct state *st, unsigned int ticks)
-{
-	if (message_update(st->data, ticks))
-		game_quit();
-}
-
-static void
-draw(struct state *st)
-{
-	painter_set_color(0xffffffff);
-	painter_clear();
-	message_draw(st->data);
-	painter_present();
-}
-
-static void
-run(struct message *msg)
-{
-	struct state state = {
-		.data = msg,
-		.handle = handle,
-		.update = update,
-		.draw = draw
-	};
-
-	message_start(msg);
-
-	game_push(&state);
-	game_loop();
-}
-
-static void
-my_draw_frame(const struct theme *th, const struct frame *f)
-{
-	(void)th;
-
-	painter_set_color(0xff0000ff);
-	painter_draw_rectangle(f->x, f->y, f->w, f->h);
-}
-
-static void
-basic(void)
-{
-	struct message msg = {
-		.x = MX,
-		.y = MY,
-		.w = MW,
-		.spacing = 12,
-		.text = {
-			"This is a basic message.",
-			"Vertical spacing is automatically computed.",
-			"You need to press <Enter> to close it.",
-		},
-	};
-
-	message_query(&msg, NULL, &msg.h);
-	run(&msg);
-}
-
-static void
-automatic(void)
-{
-	struct message msg = {
-		.x = MX,
-		.y = MY,
-		.w = MW,
-		.timeout = MESSAGE_TIMEOUT_DEFAULT,
-		.text = {
-			"This is a an automatic message.",
-			"It will disappear in a few seconds.",
-			"You can still press <Enter> to close it quicker."
-		},
-		.flags = MESSAGE_FLAGS_AUTOMATIC
-	};
-
-	message_query(&msg, NULL, &msg.h);
-	run(&msg);
-}
-
-static void
-fadein(void)
-{
-	struct message msg = {
-		.x = MX,
-		.y = MY,
-		.w = MW,
-		.delay = MESSAGE_DELAY_DEFAULT,
-		.text = {
-			"This message will fade in."
-		},
-		.flags = MESSAGE_FLAGS_FADEIN
-	};
-
-	message_query(&msg, NULL, &msg.h);
-	run(&msg);
-}
-
-static void
-fadeout(void)
-{
-	struct message msg = {
-		.x = MX,
-		.y = MY,
-		.w = MW,
-		.delay = MESSAGE_DELAY_DEFAULT,
-		.text = {
-			"This message will fade out."
-		},
-		.flags = MESSAGE_FLAGS_FADEOUT
-	};
-
-	message_query(&msg, NULL, &msg.h);
-	run(&msg);
-}
-
-static void
-fade(void)
-{
-	struct message msg = {
-		.x = MX,
-		.y = MY,
-		.w = MW,
-		.delay = MESSAGE_DELAY_DEFAULT,
-		.text = {
-			"This message will fade in and out."
-		},
-		.flags = MESSAGE_FLAGS_FADEIN | MESSAGE_FLAGS_FADEOUT
-	};
-
-	message_query(&msg, NULL, &msg.h);
-	run(&msg);
-}
-
-static void
-question(void)
-{
-	struct message msg = {
-		.x = MX,
-		.y = MY,
-		.w = MW,
-		.text = {
-			"Okay, I've understood.",
-			"Nevermind, I'll do it again."
-		},
-		.flags = MESSAGE_FLAGS_QUESTION
-	};
-
-	message_query(&msg, NULL, &msg.h);
-	run(&msg);
-}
-
-static void
-smallbottom(void)
-{
-	const unsigned int w = window.w / 4;
-	const unsigned int h = MH;
-	const int x = (window.w / 2) - (w / 2);
-	const int y = (window.h - h - 10);
-
-	struct message msg = {
-		.x = x,
-		.y = y,
-		.w = w,
-		.h = h,
-		.delay = MESSAGE_DELAY_DEFAULT,
-		.flags = MESSAGE_FLAGS_FADEIN | MESSAGE_FLAGS_FADEOUT,
-		.text = {
-			"This one is small here."
-		}
-	};
-
-	run(&msg);
-}
-
-static void
-toosmallh(void)
-{
-	struct message msg = {
-		.x = MX,
-		.y = MY,
-		.w = MW,
-		.h = 40,
-		.text = {
-			"This one is too small in height and will emit a warning.",
-			"Because this line will be incomplete."
-		},
-	};
-
-	run(&msg);
-}
-
-static void
-toosmallw(void)
-{
-	struct message msg = {
-		.x = MX,
-		.y = MY,
-		.w = 160,
-		.h = MH,
-		.text = {
-			"This one is too small in width."
-		},
-	};
-
-	run(&msg);
-}
-
-static void
-custom(void)
-{
-	struct theme theme;
-	struct message msg = {
-		.x = MX,
-		.y = MY,
-		.w = MW,
-		.h = MH,
-		.text = {
-			"This one will destroy your eyes.",
-			"Because it use a terrible custom theme."
-		},
-		.theme = &theme
-	};
-
-	/* Borrow default theme and change its frame drawing. */
-	theme_shallow(&theme, NULL);
-	theme.draw_frame = my_draw_frame;
-	theme.colors[THEME_COLOR_NORMAL] = 0x0000ffff;
-
-	run(&msg);
-}
-
-int
-main(int argc, char **argv)
-{
-	(void)argc;
-	(void)argv;
-
-	init();
-	basic();
-	fadein();
-	fadeout();
-	fade();
-	automatic();
-	question();
-	smallbottom();
-	toosmallh();
-	toosmallw();
-	custom();
-	quit();
-}
--- a/src/examples/example-sprite/main.c	Fri Oct 01 15:04:00 2021 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,161 +0,0 @@
-/*
- * example-sprite.c -- example on how to use sprites
- *
- * Copyright (c) 2020-2021 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <stdio.h>
-
-#include <core/core.h>
-#include <core/event.h>
-#include <core/game.h>
-#include <core/image.h>
-#include <core/key.h>
-#include <core/painter.h>
-#include <core/panic.h>
-#include <core/image.h>
-#include <core/sprite.h>
-#include <core/state.h>
-#include <core/sys.h>
-#include <core/texture.h>
-#include <core/util.h>
-#include <core/window.h>
-
-#include <ui/align.h>
-#include <ui/label.h>
-#include <ui/ui.h>
-
-#define W       1280
-#define H       720
-#define PATH(r) util_pathf("%s/mlk-adventure/%s", sys_dir(SYS_DIR_DATA), r)
-#define HEADER "Keys: <Left>/<Right> and <Up/Down> to select a column/row. Current: %u, %u (total %u/%u)"
-
-static char msg[512];
-static struct texture texture;
-static struct sprite sprite;
-static unsigned int row, column;
-
-static struct label help = {
-	.x = 10,
-	.y = 10,
-	.text = msg,
-	.flags = LABEL_FLAGS_SHADOW
-};
-
-static void
-changed(void)
-{
-	snprintf(msg, sizeof (msg), HEADER, column, row, sprite.ncols, sprite.nrows);
-}
-
-static void
-init(void)
-{
-	if (core_init("fr.malikania", "sprite") < 0 || ui_init() < 0)
-		panic();
-	if (window_open("Example - Sprite", W, H) < 0)
-		panic();
-	if (image_open(&texture, PATH("sprites/people.png")) < 0)
-		panic();
-
-	sprite_init(&sprite, &texture, 48, 48);
-}
-
-static void
-handle(struct state *st, const union event *ev)
-{
-	(void)st;
-
-	switch (ev->type) {
-	case EVENT_KEYDOWN:
-		switch (ev->key.key) {
-		case KEY_LEFT:
-			if (column > 0)
-				column--;
-			break;
-		case KEY_RIGHT:
-			if (column + 1 < sprite.ncols)
-				column++;
-			break;
-		case KEY_UP:
-			if (row > 0)
-				row--;
-			break;
-		case KEY_DOWN:
-			if (row + 1 < sprite.nrows)
-				row++;
-			break;
-		default:
-			break;
-		}
-
-		changed();
-		break;
-	case EVENT_QUIT:
-		game_quit();
-		break;
-	default:
-		break;
-	}
-}
-
-static void
-draw(struct state *st)
-{
-	(void)st;
-
-	int x, y;
-
-	painter_set_color(0xebede9ff);
-	painter_clear();
-	align(ALIGN_CENTER, &x, &y, sprite.cellw, sprite.cellh, 0, 0, W, H);
-	sprite_draw(&sprite, row, column, x, y);
-	label_draw(&help);
-	painter_present();
-}
-
-static void
-run(void)
-{
-	struct state state = {
-		.handle = handle,
-		.draw = draw
-	};
-
-	changed();
-
-	game_push(&state);
-	game_loop();
-}
-
-static void
-quit(void)
-{
-	texture_finish(&texture);
-	window_finish();
-	ui_finish();
-	core_finish();
-}
-
-int
-main(int argc, char **argv)
-{
-	(void)argc;
-	(void)argv;
-
-	init();
-	run();
-	quit();
-}
--- a/src/examples/example-trace/main.c	Fri Oct 01 15:04:00 2021 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,126 +0,0 @@
-/*
- * example-trace.c -- example on how to use custom trace handlers
- *
- * Copyright (c) 2020-2021 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <core/core.h>
-#include <core/event.h>
-#include <core/game.h>
-#include <core/sys.h>
-#include <core/window.h>
-#include <core/painter.h>
-#include <core/panic.h>
-#include <core/state.h>
-#include <core/trace.h>
-#include <core/util.h>
-
-#include <ui/theme.h>
-#include <ui/ui.h>
-
-#include <adventure/trace_hud.h>
-
-#define W 1280
-#define H 720
-
-static void
-init(void)
-{
-	if (core_init("fr.malikania", "trace") < 0 || ui_init() < 0)
-		panic();
-	if (window_open("Example - Trace", W, H) < 0)
-		panic();
-
-	trace_handler = trace_hud_handler;
-}
-
-static void
-handle(struct state *st, const union event *ev)
-{
-	(void)st;
-
-	switch (ev->type) {
-	case EVENT_KEYDOWN:
-		switch (ev->key.key) {
-		case KEY_ESCAPE:
-			trace_hud_clear();
-			break;
-		default:
-			tracef("keydown pressed: %d", ev->key.key);
-			break;
-		}
-		break;
-	case EVENT_CLICKDOWN:
-		tracef("click at %d,%d", ev->click.x, ev->click.y);
-		break;
-	case EVENT_QUIT:
-		game_quit();
-		break;
-	default:
-		break;
-	}
-}
-
-static void
-update(struct state *st, unsigned int ticks)
-{
-	(void)st;
-
-	trace_hud_update(ticks);
-}
-
-static void
-draw(struct state *st)
-{
-	(void)st;
-
-	painter_set_color(0x4f8fbaff);
-	painter_clear();
-	trace_hud_draw();
-	painter_present();
-}
-
-static void
-run(void)
-{
-	struct state state = {
-		.handle = handle,
-		.update = update,
-		.draw = draw
-	};
-
-	game_push(&state);
-	game_loop();
-}
-
-static void
-quit(void)
-{
-	window_finish();
-	ui_finish();
-	core_finish();
-}
-
-int
-main(int argc, char **argv)
-{
-	(void)argc;
-	(void)argv;
-
-	init();
-	run();
-	quit();
-}
-
--- a/src/examples/example-ui/main.c	Fri Oct 01 15:04:00 2021 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,324 +0,0 @@
-/*
- * example-action.c -- example on how to use automatic actions
- *
- * Copyright (c) 2020-2021 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <core/action.h>
-#include <core/core.h>
-#include <core/event.h>
-#include <core/game.h>
-#include <core/maths.h>
-#include <core/panic.h>
-#include <core/painter.h>
-#include <core/state.h>
-#include <core/sys.h>
-#include <core/util.h>
-#include <core/window.h>
-
-#include <ui/align.h>
-#include <ui/button.h>
-#include <ui/checkbox.h>
-#include <ui/frame.h>
-#include <ui/label.h>
-#include <ui/theme.h>
-#include <ui/ui.h>
-
-#define W               (1280)
-#define H               (720)
-
-#define FRAME_ORIGIN_X  (10)
-#define FRAME_ORIGIN_Y  (10)
-#define FRAME_WIDTH     (400)
-#define FRAME_HEIGHT    (200)
-
-#define HEADER_HEIGHT   (32)
-
-#define ELEMENT_HEIGHT  (20)
-
-/*
- * We design a basic UI like this.
- *
- *                    FRAME_WIDTH
- * +---------------------------------------------+--
- * | Title                                       | | HEADER_HEIGHT
- * +---------------------------------------------+--
- * | [x] Auto save                               |
- * |                                             |
- * |                                             |
- * |                                    [ Quit ] |
- * +---------------------------------------------+
- */
-static struct {
-	struct action_stack st;
-
-	struct {
-		int active;
-		int x;
-		int y;
-	} motion;
-
-	struct {
-		struct frame frame;
-		struct action act;
-	} panel;
-
-	struct {
-		struct label label;
-		struct action act;
-	} header;
-
-	struct {
-		struct checkbox cb;
-		struct action cb_act;
-		struct label label;
-		struct action label_act;
-	} autosave;
-
-	struct {
-		struct button button;
-		struct action act;
-	} quit;
-} ui = {
-	.panel = {
-		.frame = {
-			.x = FRAME_ORIGIN_X,
-			.y = FRAME_ORIGIN_Y,
-			.w = FRAME_WIDTH,
-			.h = FRAME_HEIGHT
-		}
-	},
-	.header = {
-		.label = {
-			.text = "Preferences",
-			.x = FRAME_ORIGIN_X,
-			.y = FRAME_ORIGIN_Y,
-			.flags = LABEL_FLAGS_SHADOW,
-		}
-	},
-	.autosave = {
-		.cb = {
-			.w = ELEMENT_HEIGHT,
-			.h = ELEMENT_HEIGHT
-		},
-		.label = {
-			.text = "Auto save game",
-			.flags = LABEL_FLAGS_SHADOW,
-		}
-	},
-	.quit = {
-		.button = {
-			.text = "Quit",
-			.h = ELEMENT_HEIGHT
-		}
-	}
-};
-
-static void
-init(void)
-{
-	if (core_init("fr.malikania", "ui") < 0 || ui_init() < 0)
-		panic();
-	if (window_open("Example - UI", W, H) < 0)
-		panic();
-}
-
-static void
-resize_header(void)
-{
-	struct frame *f = &ui.panel.frame;
-	struct label *l = &ui.header.label;
-	unsigned int w, h;
-
-	/* Header. */
-	label_query(l, &w, &h);
-	align(ALIGN_LEFT, &l->x, &l->y, w, h, f->x, f->y, f->w, HEADER_HEIGHT);
-
-	l->x += theme_default()->padding;
-}
-
-static void
-resize_autosave(void)
-{
-	unsigned int padding = theme_default()->padding;
-	struct frame *f = &ui.panel.frame;
-	struct checkbox *c = &ui.autosave.cb;
-	struct label *l = &ui.autosave.label;
-
-	c->x = f->x + padding;
-	c->y = f->y + HEADER_HEIGHT + padding;
-
-	l->x = c->x + c->w + padding;
-	l->y = c->y;
-}
-
-static void
-resize_button(void)
-{
-	unsigned int padding = theme_default()->padding;
-	struct frame *f = &ui.panel.frame;
-	struct button *b = &ui.quit.button;
-
-	/* Button. */
-	b->w = f->w / 4;
-
-	align(ALIGN_BOTTOM_RIGHT, &b->x, &b->y, b->w, b->h,
-	    f->x, f->y, f->w, f->h);
-
-	b->x -= padding;
-	b->y -= padding;
-}
-
-static void
-resize(void)
-{
-	resize_header();
-	resize_autosave();
-	resize_button();
-}
-
-static void
-prepare(void)
-{
-	/* Frame. */
-	frame_action(&ui.panel.frame, &ui.panel.act);
-
-	/* Header title. */
-	label_action(&ui.header.label, &ui.header.act);
-
-	/* Button quit. */
-	button_action(&ui.quit.button, &ui.quit.act);
-
-	/* Autosave. */
-	checkbox_action(&ui.autosave.cb, &ui.autosave.cb_act);
-	label_action(&ui.autosave.label, &ui.autosave.label_act);
-
-	/* Add all UI elements. */
-	action_stack_add(&ui.st, &ui.panel.act);
-	action_stack_add(&ui.st, &ui.header.act);
-	action_stack_add(&ui.st, &ui.autosave.cb_act);
-	action_stack_add(&ui.st, &ui.autosave.label_act);
-	action_stack_add(&ui.st, &ui.quit.act);
-}
-
-static int
-headerclick(int x, int y)
-{
-	return maths_is_boxed(
-	    ui.panel.frame.x,
-	    ui.panel.frame.y,
-	    ui.panel.frame.w,
-	    HEADER_HEIGHT,
-	    x,
-	    y
-	);
-}
-
-static void
-handle(struct state *st, const union event *ev)
-{
-	(void)st;
-
-	switch (ev->type) {
-	case EVENT_QUIT:
-		game_quit();
-		break;
-	case EVENT_MOUSE:
-		if (ui.motion.active) {
-			ui.panel.frame.x += ev->mouse.x - ui.motion.x;
-			ui.panel.frame.y += ev->mouse.y - ui.motion.y;
-			ui.motion.x = ev->mouse.x;
-			ui.motion.y = ev->mouse.y;
-			resize();
-		}
-		break;
-	case EVENT_CLICKDOWN:
-		if (headerclick(ev->click.x, ev->click.y)) {
-			ui.motion.active = 1;
-			ui.motion.x = ev->click.x;
-			ui.motion.y = ev->click.y;
-			window_set_cursor(WINDOW_CURSOR_SIZE);
-		}
-		else
-			action_stack_handle(&ui.st, ev);
-		break;
-	case EVENT_CLICKUP:
-		ui.motion.active = 0;
-		window_set_cursor(WINDOW_CURSOR_ARROW);
-		/* Fallthrough. */
-	default:
-		action_stack_handle(&ui.st, ev);
-		break;
-	}
-}
-
-static void
-update(struct state *st, unsigned int ticks)
-{
-	(void)st;
-
-	if (ui.quit.button.state == BUTTON_STATE_ACTIVATED)
-		game_quit();
-	else
-		action_stack_update(&ui.st, ticks);
-}
-
-static void
-draw(struct state *st)
-{
-	(void)st;
-
-	painter_set_color(0xffffffff);
-	painter_clear();
-	action_stack_draw(&ui.st);
-	painter_present();
-}
-
-static void
-run(void)
-{
-	struct state state = {
-		.handle = handle,
-		.update = update,
-		.draw = draw
-	};
-
-	prepare();
-	resize();
-
-	game_push(&state);
-	game_loop();
-}
-
-static void
-quit(void)
-{
-	window_finish();
-	ui_finish();
-	core_finish();
-}
-
-int
-main(int argc, char **argv)
-{
-	(void)argc;
-	(void)argv;
-
-	init();
-	run();
-	quit();
-
-	return 0;
-}