changeset 459:541cb950997b

examples: add libmlk-example library
author David Demelier <markand@malikania.fr>
date Sun, 26 Feb 2023 19:44:47 +0100
parents 02c1481b7dbb
children 8fa69c770569
files CMakeLists.txt examples/assets/images/battle-background.png examples/assets/images/black-cat.png examples/assets/images/haunted-wood.png examples/assets/images/sword.png examples/assets/music/vabsounds-romance.ogg examples/assets/sounds/fire.wav examples/assets/sprites/chest.png examples/assets/sprites/explosion.png examples/assets/sprites/john-sword.png examples/assets/sprites/john-walk.png examples/assets/sprites/numbers.png examples/assets/sprites/people.png examples/assets/sprites/ui-cursor.png examples/example-action/CMakeLists.txt examples/example-action/example-action.c examples/example-animation/CMakeLists.txt examples/example-animation/example-animation.c examples/example-audio/CMakeLists.txt examples/example-audio/example-audio.c examples/example-battle/CMakeLists.txt examples/example-battle/example-battle.c examples/example-battle/main.c examples/example-cursor/CMakeLists.txt examples/example-cursor/example-cursor.c examples/example-debug/CMakeLists.txt examples/example-debug/example-debug.c examples/example-drawable/CMakeLists.txt examples/example-drawable/example-drawable.c examples/example-font/CMakeLists.txt examples/example-font/example-font.c examples/example-gridmenu/CMakeLists.txt examples/example-gridmenu/example-gridmenu.c examples/example-label/CMakeLists.txt examples/example-label/example-label.c examples/example-message/CMakeLists.txt examples/example-message/example-message.c examples/example-notify/CMakeLists.txt examples/example-notify/example-notify.c examples/example-sprite/CMakeLists.txt examples/example-sprite/example-sprite.c examples/example-trace/CMakeLists.txt examples/example-trace/example-trace.c examples/example-ui/CMakeLists.txt examples/example-ui/example-ui.c examples/example/character-john.c examples/example/character-john.h examples/example/registry.c examples/example/registry.h examples/example/spell-fire.c examples/example/spell-fire.h examples/example/trace_hud.c examples/example/trace_hud.h libmlk-example/CMakeLists.txt libmlk-example/assets/images/battle-background.png libmlk-example/assets/images/black-cat.png libmlk-example/assets/images/haunted-wood.png libmlk-example/assets/images/sword.png libmlk-example/assets/music/vabsounds-romance.ogg libmlk-example/assets/sounds/fire.wav libmlk-example/assets/sprites/chest.png libmlk-example/assets/sprites/explosion.png libmlk-example/assets/sprites/john-sword.png libmlk-example/assets/sprites/john-walk.png libmlk-example/assets/sprites/numbers.png libmlk-example/assets/sprites/people.png libmlk-example/assets/sprites/ui-cursor.png libmlk-example/mlk/example/character-john.c libmlk-example/mlk/example/character-john.h libmlk-example/mlk/example/registry.c libmlk-example/mlk/example/registry.h libmlk-example/mlk/example/spell-fire.c libmlk-example/mlk/example/spell-fire.h libmlk-example/mlk/example/trace-hud.c libmlk-example/mlk/example/trace-hud.h
diffstat 75 files changed, 1327 insertions(+), 1323 deletions(-) [+]
line wrap: on
line diff
--- a/CMakeLists.txt	Fri Feb 24 22:14:42 2023 +0100
+++ b/CMakeLists.txt	Sun Feb 26 19:44:47 2023 +0100
@@ -86,5 +86,6 @@
 endif ()
 
 if (MLK_WITH_EXAMPLES)
+	add_subdirectory(libmlk-example)
 	add_subdirectory(examples)
 endif ()
Binary file examples/assets/images/battle-background.png has changed
Binary file examples/assets/images/black-cat.png has changed
Binary file examples/assets/images/haunted-wood.png has changed
Binary file examples/assets/images/sword.png has changed
Binary file examples/assets/music/vabsounds-romance.ogg has changed
Binary file examples/assets/sounds/fire.wav has changed
Binary file examples/assets/sprites/chest.png has changed
Binary file examples/assets/sprites/explosion.png has changed
Binary file examples/assets/sprites/john-sword.png has changed
Binary file examples/assets/sprites/john-walk.png has changed
Binary file examples/assets/sprites/numbers.png has changed
Binary file examples/assets/sprites/people.png has changed
Binary file examples/assets/sprites/ui-cursor.png has changed
--- a/examples/example-action/CMakeLists.txt	Fri Feb 24 22:14:42 2023 +0100
+++ b/examples/example-action/CMakeLists.txt	Sun Feb 26 19:44:47 2023 +0100
@@ -20,21 +20,14 @@
 
 set(
 	SOURCES
-	${example-action_SOURCE_DIR}/main.c
-)
-
-set(
-	ASSETS
-	${example-action_SOURCE_DIR}/assets/sprites/chest.png
-	${example-action_SOURCE_DIR}/assets/sprites/people.png
+	${example-action_SOURCE_DIR}/example-action.c
 )
 
 mlk_executable(
 	NAME example-action
 	FOLDER examples
-	LIBRARIES libmlk-rpg
-	SOURCES ${ASSETS} ${SOURCES}
-	ASSETS ${ASSETS}
+	LIBRARIES libmlk-example
+	SOURCES ${SOURCES}
 )
 
-source_group(TREE ${example-action_SOURCE_DIR} FILES ${ASSETS} ${SOURCES})
+source_group(TREE ${example-action_SOURCE_DIR} FILES ${SOURCES})
--- a/examples/example-action/example-action.c	Fri Feb 24 22:14:42 2023 +0100
+++ b/examples/example-action/example-action.c	Sun Feb 26 19:44:47 2023 +0100
@@ -53,11 +53,11 @@
 
 /* This is a stack of "parallel" events. */
 static struct mlk_action *events_actions[32];
-static struct action_stack events;
+static struct mlk_action_stack events;
 
 /* This is a stack of modal events. */
 static struct mlk_action *modal_actions[32];
-static struct action_stack modal;
+static struct mlk_action_stack modal;
 
 /* Maximum number of states. */
 static struct state *states[1];
@@ -218,7 +218,7 @@
 
 	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);
+	mlk_action_stack_add(&modal, &guide.msgs[index].act);
 }
 
 static void
@@ -240,20 +240,20 @@
 	script_append(&guide.script, &guide.response);
 
 	script_action(&guide.script, &guide.script_act);
-	action_stack_add(&modal, &guide.script_act);
+	mlk_action_stack_add(&modal, &guide.script_act);
 }
 
 static void
-guide_handle(struct mlk_action *act, const union event *ev)
+guide_handle(struct mlk_action *act, const union mlk_event *ev)
 {
 	(void)act;
 
-	if (!action_stack_completed(&modal))
+	if (!mlk_action_stack_completed(&modal))
 		return;
 
 	switch (ev->type) {
-	case EVENT_CLICKDOWN:
-		if (maths_is_boxed(guide.x, guide.y,
+	case MLK_EVENT_CLICKDOWN:
+		if (mlk_maths_is_boxed(guide.x, guide.y,
 		    guide.sprite.cellw, guide.sprite.cellh,
 		    ev->click.x, ev->click.y))
 			guide_popup();
@@ -274,7 +274,7 @@
 static void
 guide_init(void)
 {
-	if (image_openmem(&guide.image, assets_people, sizeof (assets_people)) < 0)
+	if (mlk_image_openmem(&guide.image, assets_sprites_people, sizeof (assets_sprites_people)) < 0)
 		panic();
 
 	sprite_init(&guide.sprite, &guide.image, 48, 48);
@@ -287,21 +287,21 @@
 }
 
 static void
-chest_handle(struct mlk_action *act, const union event *ev)
+chest_handle(struct mlk_action *act, const union mlk_event *ev)
 {
 	(void)act;
 
-	if (chest.opened || !action_stack_completed(&modal))
+	if (chest.opened || !mlk_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,
+	case MLK_EVENT_CLICKDOWN:
+		if (mlk_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);
+			mlk_action_stack_add(&modal, &chest.msg_act);
 		}
 	default:
 		break;
@@ -322,7 +322,7 @@
 static void
 chest_init(void)
 {
-	if (image_openmem(&chest.image, assets_chest, sizeof (assets_chest)) < 0)
+	if (mlk_image_openmem(&chest.image, assets_sprites_chest, sizeof (assets_sprites_chest)) < 0)
 		panic();
 
 	sprite_init(&chest.sprite, &chest.image, 32, 32);
@@ -346,17 +346,17 @@
 }
 
 static void
-handle(struct state *st, const union event *ev)
+handle(struct state *st, const union mlk_event *ev)
 {
 	(void)st;
 
 	switch (ev->type) {
-	case EVENT_QUIT:
-		game_quit();
+	case MLK_EVENT_QUIT:
+		mlk_game_quit();
 		break;
 	default:
-		action_stack_handle(&events, ev);
-		action_stack_handle(&modal, ev);
+		mlk_action_stack_handle(&events, ev);
+		mlk_action_stack_handle(&modal, ev);
 		break;
 	}
 }
@@ -366,8 +366,8 @@
 {
 	(void)st;
 
-	action_stack_update(&events, ticks);
-	action_stack_update(&modal, ticks);
+	mlk_action_stack_update(&events, ticks);
+	mlk_action_stack_update(&modal, ticks);
 }
 
 static void
@@ -375,11 +375,11 @@
 {
 	(void)st;
 
-	painter_set_color(0xffffffff);
-	painter_clear();
-	action_stack_draw(&events);
-	action_stack_draw(&modal);
-	painter_present();
+	mlk_painter_set_color(0xffffffff);
+	mlk_painter_clear();
+	mlk_action_stack_draw(&events);
+	mlk_action_stack_draw(&modal);
+	mlk_painter_present();
 }
 
 static void
@@ -391,15 +391,14 @@
 		.draw = draw
 	};
 
-	action_stack_init(&modal, modal_actions, UTIL_SIZE(modal_actions));
+	mlk_action_stack_init(&modal, modal_actions, UTIL_SIZE(modal_actions));
+	mlk_action_stack_init(&events, events_actions, UTIL_SIZE(events_actions));
+	mlk_action_stack_add(&events, &chest.event);
+	mlk_action_stack_add(&events, &guide.event);
 
-	action_stack_init(&events, events_actions, UTIL_SIZE(events_actions));
-	action_stack_add(&events, &chest.event);
-	action_stack_add(&events, &guide.event);
-
-	game_init(states, UTIL_SIZE(states));
-	game_push(&state);
-	game_loop();
+	mlk_game_init(states, UTIL_SIZE(states));
+	mlk_game_push(&state);
+	mlk_game_loop();
 }
 
 static void
--- a/examples/example-animation/CMakeLists.txt	Fri Feb 24 22:14:42 2023 +0100
+++ b/examples/example-animation/CMakeLists.txt	Sun Feb 26 19:44:47 2023 +0100
@@ -20,20 +20,14 @@
 
 set(
 	SOURCES
-	${example-animation_SOURCE_DIR}/main.c
-)
-
-set(
-	ASSETS
-	${example-animation_SOURCE_DIR}/assets/sprites/numbers.png
+	${example-animation_SOURCE_DIR}/example-animation.c
 )
 
 mlk_executable(
 	NAME example-animation
 	FOLDER examples
-	LIBRARIES libmlk-ui
-	SOURCES ${ASSETS} ${SOURCES}
-	ASSETS ${ASSETS}
+	LIBRARIES libmlk-example
+	SOURCES ${SOURCES}
 )
 
-source_group(TREE ${example-animation_SOURCE_DIR} FILES ${ASSETS} ${SOURCES})
+source_group(TREE ${example-animation_SOURCE_DIR} FILES ${SOURCES})
--- a/examples/example-animation/example-animation.c	Fri Feb 24 22:14:42 2023 +0100
+++ b/examples/example-animation/example-animation.c	Sun Feb 26 19:44:47 2023 +0100
@@ -47,7 +47,7 @@
 
 static struct state *states[1];
 static struct texture numbers;
-static struct animation animation;
+static struct mlk_animation animation;
 static struct sprite sprite;
 static int completed = 1;
 
@@ -58,28 +58,28 @@
 		panic();
 	if (window_open("Example - Animation", W, H) < 0)
 		panic();
-	if (image_openmem(&numbers, assets_numbers, sizeof (assets_numbers)) < 0)
+	if (mlk_image_openmem(&numbers, assets_sprites_numbers, sizeof (assets_sprites_numbers)) < 0)
 		panic();
 }
 
 static void
-handle(struct state *st, const union event *ev)
+handle(struct state *st, const union mlk_event *ev)
 {
 	(void)st;
 
 	switch (ev->type) {
-	case EVENT_KEYDOWN:
+	case MLK_EVENT_KEYDOWN:
 		switch (ev->key.key) {
-		case KEY_SPACE:
-			animation_start(&animation);
-			completed = animation_completed(&animation);
+		case MLK_KEY_SPACE:
+			mlk_animation_start(&animation);
+			completed = mlk_animation_completed(&animation);
 			break;
 		default:
 			break;
 		}
 		break;
-	case EVENT_QUIT:
-		game_quit();
+	case MLK_EVENT_QUIT:
+		mlk_game_quit();
 		break;
 	default:
 		break;
@@ -92,7 +92,7 @@
 	(void)st;
 
 	if (!completed)
-		completed = animation_update(&animation, ticks);
+		completed = mlk_animation_update(&animation, ticks);
 }
 
 static void
@@ -100,14 +100,14 @@
 {
 	(void)st;
 
-	painter_set_color(0x4f8fbaff);
-	painter_clear();
+	mlk_painter_set_color(0x4f8fbaff);
+	mlk_painter_clear();
 	label_draw(&label);
 
 	if (!completed)
-		animation_draw(&animation, (window.w - sprite.cellw) / 2, (window.h - sprite.cellh) / 2);
+		mlk_animation_draw(&animation, (window.w - sprite.cellw) / 2, (window.h - sprite.cellh) / 2);
 
-	painter_present();
+	mlk_painter_present();
 }
 
 static void
@@ -120,11 +120,11 @@
 	};
 
 	sprite_init(&sprite, &numbers, 48, 48);
-	animation_init(&animation, &sprite, 1000);
+	mlk_animation_init(&animation, &sprite, 1000);
 
-	game_init(states, UTIL_SIZE(states));
-	game_push(&state);
-	game_loop();
+	mlk_game_init(states, UTIL_SIZE(states));
+	mlk_game_push(&state);
+	mlk_game_loop();
 }
 
 static void
--- a/examples/example-audio/CMakeLists.txt	Fri Feb 24 22:14:42 2023 +0100
+++ b/examples/example-audio/CMakeLists.txt	Sun Feb 26 19:44:47 2023 +0100
@@ -20,21 +20,14 @@
 
 set(
 	SOURCES
-	${example-audio_SOURCE_DIR}/main.c
-)
-
-set(
-	ASSETS
-	${example-audio_SOURCE_DIR}/assets/music/vabsounds-romance.ogg
-	${example-audio_SOURCE_DIR}/assets/sounds/fire.wav
+	${example-audio_SOURCE_DIR}/example-audio.c
 )
 
 mlk_executable(
 	NAME example-audio
 	FOLDER examples
-	LIBRARIES libmlk-ui
+	LIBRARIES libmlk-example
 	SOURCES ${ASSETS} ${SOURCES}
-	ASSETS ${ASSETS}
 )
 
-source_group(TREE ${example-audio_SOURCE_DIR} FILES ${ASSETS} ${SOURCES})
+source_group(TREE ${example-audio_SOURCE_DIR} FILES ${SOURCES})
--- a/examples/example-audio/example-audio.c	Fri Feb 24 22:14:42 2023 +0100
+++ b/examples/example-audio/example-audio.c	Sun Feb 26 19:44:47 2023 +0100
@@ -39,7 +39,7 @@
 #define H       720
 
 static struct state *states[1];
-static struct music music;
+static struct mlk_music music;
 static struct sound sound;
 
 static struct label label_music = {
@@ -63,8 +63,8 @@
 		panic();
 	if (window_open("Example - Audio", W, H) < 0)
 		panic();
-	if (music_openmem(&music, assets_vabsounds_romance, sizeof (assets_vabsounds_romance)) < 0 ||
-	    sound_openmem(&sound, assets_fire, sizeof (assets_fire)) < 0)
+	if (mlk_music_openmem(&music, assets_music_vabsounds_romance, sizeof (assets_music_vabsounds_romance)) < 0 ||
+	    sound_openmem(&sound, assets_sounds_fire, sizeof (assets_sounds_fire)) < 0)
 		panic();
 }
 
@@ -77,38 +77,38 @@
 }
 
 static void
-handle(struct state *st, const union event *ev)
+handle(struct state *st, const union mlk_event *ev)
 {
 	(void)st;
 
 	switch (ev->type) {
-	case EVENT_CLICKDOWN:
+	case MLK_EVENT_CLICKDOWN:
 		if (sound_play(&sound) < 0)
 			panic();
 		break;
-	case EVENT_KEYDOWN:
+	case MLK_EVENT_KEYDOWN:
 		switch (ev->key.key) {
-		case KEY_p:
-			music_pause(&music);
+		case MLK_KEY_p:
+			mlk_music_pause(&music);
 			break;
-		case KEY_r:
-			music_resume(&music);
+		case MLK_KEY_r:
+			mlk_music_resume(&music);
 			break;
-		case KEY_q:
-			music_stop(&music);
+		case MLK_KEY_q:
+			mlk_music_stop(&music);
 			break;
-		case KEY_l:
-			music_play(&music, MUSIC_LOOP);
+		case MLK_KEY_l:
+			mlk_music_play(&music, MUSIC_LOOP);
 			break;
-		case KEY_SPACE:
-			music_play(&music, 0);
+		case MLK_KEY_SPACE:
+			mlk_music_play(&music, 0);
 			break;
 		default:
 			break;
 		}
 		break;
-	case EVENT_QUIT:
-		game_quit();
+	case MLK_EVENT_QUIT:
+		mlk_game_quit();
 		break;
 	default:
 		break;
@@ -120,11 +120,11 @@
 {
 	(void)st;
 
-	painter_set_color(0x006554ff);
-	painter_clear();
+	mlk_painter_set_color(0x006554ff);
+	mlk_painter_clear();
 	label_draw(&label_music);
 	label_draw(&label_sound);
-	painter_present();
+	mlk_painter_present();
 }
 
 static void
@@ -135,11 +135,11 @@
 		.draw = draw
 	};
 
-	game_init(states, UTIL_SIZE(states));
-	game_push(&state);
-	game_loop();
+	mlk_game_init(states, UTIL_SIZE(states));
+	mlk_game_push(&state);
+	mlk_game_loop();
 
-	music_finish(&music);
+	mlk_music_finish(&music);
 	sound_finish(&sound);
 }
 
--- a/examples/example-battle/CMakeLists.txt	Fri Feb 24 22:14:42 2023 +0100
+++ b/examples/example-battle/CMakeLists.txt	Sun Feb 26 19:44:47 2023 +0100
@@ -20,33 +20,14 @@
 
 set(
 	SOURCES
-	${example-battle_SOURCE_DIR}/character-john.c
-	${example-battle_SOURCE_DIR}/character-john.h
-	${example-battle_SOURCE_DIR}/main.c
-	${example-battle_SOURCE_DIR}/registry.c
-	${example-battle_SOURCE_DIR}/registry.h
-	${example-battle_SOURCE_DIR}/spell-fire.c
-	${example-battle_SOURCE_DIR}/spell-fire.h
-)
-
-set(
-	ASSETS
-	${example-battle_SOURCE_DIR}/assets/images/battle-background.png
-	${example-battle_SOURCE_DIR}/assets/images/black-cat.png
-	${example-battle_SOURCE_DIR}/assets/images/haunted-wood.png
-	${example-battle_SOURCE_DIR}/assets/sounds/fire.wav
-	${example-battle_SOURCE_DIR}/assets/sprites/explosion.png
-	${example-battle_SOURCE_DIR}/assets/sprites/john-sword.png
-	${example-battle_SOURCE_DIR}/assets/sprites/john-walk.png
-	${example-battle_SOURCE_DIR}/assets/sprites/ui-cursor.png
+	${example-battle_SOURCE_DIR}/example-battle.c
 )
 
 mlk_executable(
 	NAME example-battle
 	FOLDER examples
-	LIBRARIES libmlk-rpg
-	SOURCES ${ASSETS} ${SOURCES}
-	ASSETS ${ASSETS}
+	LIBRARIES libmlk-example
+	SOURCES ${SOURCES}
 )
 
-source_group(TREE ${example-battle_SOURCE_DIR} FILES ${ASSETS} ${SOURCES})
+source_group(TREE ${example-battle_SOURCE_DIR} FILES ${SOURCES})
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/example-battle/example-battle.c	Sun Feb 26 19:44:47 2023 +0100
@@ -0,0 +1,346 @@
+/*
+ * example-battle.c -- show how to use battle
+ *
+ * Copyright (c) 2020-2023 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <mlk/core/alloc.h>
+#include <mlk/core/core.h>
+#include <mlk/core/event.h>
+#include <mlk/core/game.h>
+#include <mlk/core/image.h>
+#include <mlk/core/painter.h>
+#include <mlk/core/panic.h>
+#include <mlk/core/sprite.h>
+#include <mlk/core/state.h>
+#include <mlk/core/sys.h>
+#include <mlk/core/texture.h>
+#include <mlk/core/util.h>
+#include <mlk/core/window.h>
+
+#include <mlk/ui/align.h>
+#include <mlk/ui/label.h>
+#include <mlk/ui/theme.h>
+#include <mlk/ui/ui.h>
+
+#include <mlk/rpg/battle-bar-default.h>
+#include <mlk/rpg/battle-bar.h>
+#include <mlk/rpg/battle.h>
+#include <mlk/rpg/character.h>
+#include <mlk/rpg/rpg.h>
+#include <mlk/rpg/spell.h>
+
+#include <mlk/example/registry.h>
+#include <mlk/example/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;
+}
+
+#if 0
+
+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;
+}
+
+#endif
+
+static void
+black_cat_reset(struct character *ch)
+{
+	ch->hpmax = ch->hp = 120;
+	ch->mpmax = ch->mp = 38;
+	ch->atk = 22;
+	ch->def = 19;
+	ch->agt = 11;
+	ch->luck = 14;
+}
+
+#if 0
+
+static void
+haunted_wood_strat(struct character *ch, struct battle *bt)
+{
+	(void)ch;
+
+	/* TODO: Select randomly. */
+	battle_attack(bt, battle_current(bt)->ch, bt->team[0]->ch);
+}
+
+#endif
+
+static void
+black_cat_strat(struct character *ch, struct battle *bt)
+{
+	(void)ch;
+
+	/* TODO: Select randomly. */
+	battle_attack(bt, battle_current(bt)->ch, bt->team[0]->ch);
+}
+
+#if 0
+
+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
+};
+
+#endif
+
+static void
+init(void)
+{
+	if (core_init("fr.malikania", "example-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 *states[2];
+static struct state fight_state;
+
+static struct {
+	struct character ch;
+	struct battle_entity entity;
+} entities[] = {
+	/* == Enemies == */
+	{
+		.ch = {
+			.name = "Black Cat",
+			.level = 6,
+			.reset = black_cat_reset,
+			.sprites = {
+				[CHARACTER_SPRITE_NORMAL] = &registry_sprites[REGISTRY_TEXTURE_BLACK_CAT],
+			},
+			.exec = black_cat_strat
+		},
+		.entity = {
+			.ch = &entities[0].ch
+		}
+	},
+
+	/* == Team == */
+	{
+		.ch = {
+			.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
+			}
+		},
+		.entity = {
+			.ch = &entities[1].ch
+		}
+	},
+};
+
+static struct mlk_drawable *drawables[16];
+static struct mlk_drawable_stack drawable_stack;
+
+static struct mlk_action *actions[16];
+static struct mlk_action_stack action_stack;
+
+static struct battle_entity *entities_enemies[1];
+static struct battle_entity *entities_team[1];
+static struct battle bt;
+static struct battle_bar_default default_bar;
+static struct battle_bar bar;
+
+static void
+prepare_to_fight(void)
+{
+	mlk_action_stack_init(&action_stack, actions, UTIL_SIZE(actions));
+	mlk_drawable_stack_init(&drawable_stack, drawables, UTIL_SIZE(drawables));
+
+	battle_init(&bt);
+	battle_bar_default_init(&default_bar);
+	battle_bar_default(&default_bar, &bar);
+
+	bt.team = entities_team;
+	bt.teamsz = UTIL_SIZE(entities_team);
+
+	bt.enemies = entities_enemies;
+	bt.enemiesz = UTIL_SIZE(entities_enemies);
+
+	/* Black cat is near the previous monster. */
+	entities_team[0] = &entities[1].entity;
+	entities_enemies[0] = &entities[0].entity;
+	entities_enemies[0]->x = 500;
+	entities_enemies[0]->y = 100;
+
+	bt.background = &registry_images[REGISTRY_IMAGE_BATTLE_BACKGROUND];
+	bt.bar = &bar;
+	bt.actions = &action_stack;
+	bt.effects = &drawable_stack;
+
+	battle_start(&bt);
+	mlk_game_push(&fight_state);
+}
+
+static void
+empty_handle(struct state *st, const union mlk_event *ev)
+{
+	(void)st;
+
+	switch (ev->type) {
+	case MLK_EVENT_QUIT:
+		mlk_game_quit();
+		break;
+	case MLK_EVENT_KEYDOWN:
+		if (ev->key.key == MLK_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
+	};
+
+	mlk_painter_set_color(0x4f8fbaff);
+	mlk_painter_clear();
+	label_draw(&info);
+	mlk_painter_present();
+}
+
+static struct state empty_state = {
+	.handle = empty_handle,
+	.draw = empty_draw
+};
+
+static void
+fight_handle(struct state *st, const union mlk_event *ev)
+{
+	(void)st;
+
+	battle_handle(&bt, ev);
+}
+
+static void
+fight_update(struct state *st, unsigned int ticks)
+{
+	(void)st;
+
+	if (battle_update(&bt, ticks))
+		mlk_game_pop();
+}
+
+static void
+fight_draw(struct state *st)
+{
+	(void)st;
+
+	mlk_painter_set_color(0x000000ff);
+	mlk_painter_clear();
+	battle_draw(&bt);
+	mlk_painter_present();
+}
+
+static void
+fight_finish(struct state *st)
+{
+	(void)st;
+
+	battle_bar_finish(&bar, &bt);
+	battle_bar_default_finish(&default_bar);
+	battle_finish(&bt);
+}
+
+static struct state fight_state = {
+	.handle = fight_handle,
+	.update = fight_update,
+	.draw = fight_draw,
+	.finish = fight_finish,
+};
+
+static void
+run(void)
+{
+	mlk_game_init(states, UTIL_SIZE(states));
+	mlk_game_push(&empty_state);
+	mlk_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/examples/example-battle/main.c	Fri Feb 24 22:14:42 2023 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,346 +0,0 @@
-/*
- * example-battle.c -- show how to use battle
- *
- * Copyright (c) 2020-2023 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER 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/battle-bar-default.h>
-#include <rpg/battle-bar.h>
-#include <rpg/battle.h>
-#include <rpg/character.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;
-}
-
-#if 0
-
-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;
-}
-
-#endif
-
-static void
-black_cat_reset(struct character *ch)
-{
-	ch->hpmax = ch->hp = 120;
-	ch->mpmax = ch->mp = 38;
-	ch->atk = 22;
-	ch->def = 19;
-	ch->agt = 11;
-	ch->luck = 14;
-}
-
-#if 0
-
-static void
-haunted_wood_strat(struct character *ch, struct battle *bt)
-{
-	(void)ch;
-
-	/* TODO: Select randomly. */
-	battle_attack(bt, battle_current(bt)->ch, bt->team[0]->ch);
-}
-
-#endif
-
-static void
-black_cat_strat(struct character *ch, struct battle *bt)
-{
-	(void)ch;
-
-	/* TODO: Select randomly. */
-	battle_attack(bt, battle_current(bt)->ch, bt->team[0]->ch);
-}
-
-#if 0
-
-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
-};
-
-#endif
-
-static void
-init(void)
-{
-	if (core_init("fr.malikania", "example-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 *states[2];
-static struct state fight_state;
-
-static struct {
-	struct character ch;
-	struct battle_entity entity;
-} entities[] = {
-	/* == Enemies == */
-	{
-		.ch = {
-			.name = "Black Cat",
-			.level = 6,
-			.reset = black_cat_reset,
-			.sprites = {
-				[CHARACTER_SPRITE_NORMAL] = &registry_sprites[REGISTRY_TEXTURE_BLACK_CAT],
-			},
-			.exec = black_cat_strat
-		},
-		.entity = {
-			.ch = &entities[0].ch
-		}
-	},
-
-	/* == Team == */
-	{
-		.ch = {
-			.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
-			}
-		},
-		.entity = {
-			.ch = &entities[1].ch
-		}
-	},
-};
-
-static struct drawable *drawables[16];
-static struct drawable_stack drawable_stack;
-
-static struct action *actions[16];
-static struct action_stack action_stack;
-
-static struct battle_entity *entities_enemies[1];
-static struct battle_entity *entities_team[1];
-static struct battle bt;
-static struct battle_bar_default default_bar;
-static struct battle_bar bar;
-
-static void
-prepare_to_fight(void)
-{
-	action_stack_init(&action_stack, actions, UTIL_SIZE(actions));
-	drawable_stack_init(&drawable_stack, drawables, UTIL_SIZE(drawables));
-
-	battle_init(&bt);
-	battle_bar_default_init(&default_bar);
-	battle_bar_default(&default_bar, &bar);
-
-	bt.team = entities_team;
-	bt.teamsz = UTIL_SIZE(entities_team);
-
-	bt.enemies = entities_enemies;
-	bt.enemiesz = UTIL_SIZE(entities_enemies);
-
-	/* Black cat is near the previous monster. */
-	entities_team[0] = &entities[1].entity;
-	entities_enemies[0] = &entities[0].entity;
-	entities_enemies[0]->x = 500;
-	entities_enemies[0]->y = 100;
-
-	bt.background = &registry_images[REGISTRY_IMAGE_BATTLE_BACKGROUND];
-	bt.bar = &bar;
-	bt.actions = &action_stack;
-	bt.effects = &drawable_stack;
-
-	battle_start(&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)
-{
-	(void)st;
-
-	battle_handle(&bt, ev);
-}
-
-static void
-fight_update(struct state *st, unsigned int ticks)
-{
-	(void)st;
-
-	if (battle_update(&bt, ticks))
-		game_pop();
-}
-
-static void
-fight_draw(struct state *st)
-{
-	(void)st;
-
-	painter_set_color(0x000000ff);
-	painter_clear();
-	battle_draw(&bt);
-	painter_present();
-}
-
-static void
-fight_finish(struct state *st)
-{
-	(void)st;
-
-	battle_bar_finish(&bar, &bt);
-	battle_bar_default_finish(&default_bar);
-	battle_finish(&bt);
-}
-
-static struct state fight_state = {
-	.handle = fight_handle,
-	.update = fight_update,
-	.draw = fight_draw,
-	.finish = fight_finish,
-};
-
-static void
-run(void)
-{
-	game_init(states, UTIL_SIZE(states));
-	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/examples/example-cursor/CMakeLists.txt	Fri Feb 24 22:14:42 2023 +0100
+++ b/examples/example-cursor/CMakeLists.txt	Sun Feb 26 19:44:47 2023 +0100
@@ -20,13 +20,13 @@
 
 set(
 	SOURCES
-	${example-cursor_SOURCE_DIR}/main.c
+	${example-cursor_SOURCE_DIR}/example-cursor.c
 )
 
 mlk_executable(
 	NAME example-cursor
 	FOLDER examples
-	LIBRARIES libmlk-ui
+	LIBRARIES libmlk-example
 	SOURCES ${SOURCES}
 )
 
--- a/examples/example-cursor/example-cursor.c	Fri Feb 24 22:14:42 2023 +0100
+++ b/examples/example-cursor/example-cursor.c	Sun Feb 26 19:44:47 2023 +0100
@@ -73,18 +73,18 @@
 }
 
 static void
-handle(struct state *st, const union event *ev)
+handle(struct state *st, const union mlk_event *ev)
 {
 	(void)st;
 
 	switch (ev->type) {
-	case EVENT_KEYDOWN:
+	case MLK_EVENT_KEYDOWN:
 		switch (ev->key.key) {
-		case KEY_LEFT:
+		case MLK_KEY_LEFT:
 			if (cursor > 0)
 				change(--cursor);
 			break;
-		case KEY_RIGHT:
+		case MLK_KEY_RIGHT:
 			if (cursor + 1 < WINDOW_CURSOR_LAST)
 				change(++cursor);
 			break;
@@ -94,8 +94,8 @@
 
 
 		break;
-	case EVENT_QUIT:
-		game_quit();
+	case MLK_EVENT_QUIT:
+		mlk_game_quit();
 		break;
 	default:
 		break;
@@ -107,10 +107,10 @@
 {
 	(void)st;
 
-	painter_set_color(0xebede9ff);
-	painter_clear();
+	mlk_painter_set_color(0xebede9ff);
+	mlk_painter_clear();
 	label_draw(&help);
-	painter_present();
+	mlk_painter_present();
 }
 
 static void
@@ -123,9 +123,9 @@
 
 	change(cursor);
 
-	game_init(states, UTIL_SIZE(states));
-	game_push(&state);
-	game_loop();
+	mlk_game_init(states, UTIL_SIZE(states));
+	mlk_game_push(&state);
+	mlk_game_loop();
 }
 
 static void
--- a/examples/example-debug/CMakeLists.txt	Fri Feb 24 22:14:42 2023 +0100
+++ b/examples/example-debug/CMakeLists.txt	Sun Feb 26 19:44:47 2023 +0100
@@ -20,13 +20,13 @@
 
 set(
 	SOURCES
-	${example-debug_SOURCE_DIR}/main.c
+	${example-debug_SOURCE_DIR}/example-debug.c
 )
 
 mlk_executable(
 	NAME example-debug
 	FOLDER examples
-	LIBRARIES libmlk-ui
+	LIBRARIES libmlk-example
 	SOURCES ${SOURCES}
 )
 
--- a/examples/example-debug/example-debug.c	Fri Feb 24 22:14:42 2023 +0100
+++ b/examples/example-debug/example-debug.c	Sun Feb 26 19:44:47 2023 +0100
@@ -48,17 +48,17 @@
 }
 
 static void
-handle(struct state *st, const union event *ev)
+handle(struct state *st, const union mlk_event *ev)
 {
 	(void)st;
 
 	switch (ev->type) {
-	case EVENT_MOUSE:
+	case MLK_EVENT_MOUSE:
 		mouse_x = ev->mouse.x;
 		mouse_y = ev->mouse.y;
 		break;
-	case EVENT_QUIT:
-		game_quit();
+	case MLK_EVENT_QUIT:
+		mlk_game_quit();
 		break;
 	default:
 		break;
@@ -72,11 +72,11 @@
 
 	struct debug_report report = {0};
 
-	painter_set_color(0x4f8fbaff);
-	painter_clear();
+	mlk_painter_set_color(0x4f8fbaff);
+	mlk_painter_clear();
 	debugf(&report, "Game running.");
 	debugf(&report, "mouse: %d, %d", mouse_x, mouse_y);
-	painter_present();
+	mlk_painter_present();
 }
 
 static void
@@ -87,9 +87,9 @@
 		.draw = draw
 	};
 
-	game_init(states, UTIL_SIZE(states));
-	game_push(&state);
-	game_loop();
+	mlk_game_init(states, UTIL_SIZE(states));
+	mlk_game_push(&state);
+	mlk_game_loop();
 }
 
 static void
--- a/examples/example-drawable/CMakeLists.txt	Fri Feb 24 22:14:42 2023 +0100
+++ b/examples/example-drawable/CMakeLists.txt	Sun Feb 26 19:44:47 2023 +0100
@@ -20,20 +20,14 @@
 
 set(
 	SOURCES
-	${example-drawable_SOURCE_DIR}/main.c
-)
-
-set(
-	ASSETS
-	${example-drawable_SOURCE_DIR}/assets/sprites/explosion.png
+	${example-drawable_SOURCE_DIR}/example-drawable.c
 )
 
 mlk_executable(
 	NAME example-drawable
 	FOLDER examples
-	LIBRARIES libmlk-ui
-	SOURCES ${ASSETS} ${SOURCES}
-	ASSETS ${ASSETS}
+	LIBRARIES libmlk-example
+	SOURCES ${SOURCES}
 )
 
-source_group(TREE ${example-drawable_SOURCE_DIR} FILES ${ASSETS} ${SOURCES})
+source_group(TREE ${example-drawable_SOURCE_DIR} FILES ${SOURCES})
--- a/examples/example-drawable/example-drawable.c	Fri Feb 24 22:14:42 2023 +0100
+++ b/examples/example-drawable/example-drawable.c	Sun Feb 26 19:44:47 2023 +0100
@@ -54,8 +54,8 @@
 };
 
 static struct state *states[1];
-static struct drawable *drawables[64];
-static struct drawable_stack stack;
+static struct mlk_drawable *drawables[64];
+static struct mlk_drawable_stack stack;
 
 /*
  * List of drawables for this example.
@@ -67,8 +67,8 @@
 static struct sprite explosion_sprite;
 
 struct explosion {
-	struct animation anim;
-	struct drawable dw;
+	struct mlk_animation anim;
+	struct mlk_drawable dw;
 };
 
 static void
@@ -80,40 +80,40 @@
 		panic();
 
 	/* 0: Explosion animation. */
-	if (image_openmem(&explosion_tex, assets_explosion, sizeof (assets_explosion)) < 0)
+	if (mlk_image_openmem(&explosion_tex, assets_sprites_explosion, sizeof (assets_sprites_explosion)) < 0)
 		panic();
 
 	sprite_init(&explosion_sprite, &explosion_tex, 256, 256);
 }
 
 static int
-explosion_update(struct drawable *dw, unsigned int ticks)
+explosion_update(struct mlk_drawable *dw, unsigned int ticks)
 {
 	struct explosion *ex = dw->data;
 
-	return animation_update(&ex->anim, ticks);
+	return mlk_animation_update(&ex->anim, ticks);
 }
 
 static void
-explosion_draw(struct drawable *dw)
+explosion_draw(struct mlk_drawable *dw)
 {
 	struct explosion *ex = dw->data;
 
-	animation_draw(&ex->anim, ex->dw.x, ex->dw.y);
+	mlk_animation_draw(&ex->anim, ex->dw.x, ex->dw.y);
 }
 
 static void
-explosion_finish(struct drawable *dw)
+explosion_finish(struct mlk_drawable *dw)
 {
-	free(dw->data);
+	mlk_alloc_free(dw->data);
 }
 
 static void
 spawn(int x, int y)
 {
-	struct explosion *ex = mlk_alloc_new0(sizeof (*ex));
+	struct explosion *ex = mlk_alloc_new0(1, sizeof (*ex));
 
-	animation_init(&ex->anim, &explosion_sprite, 15);
+	mlk_animation_init(&ex->anim, &explosion_sprite, 15);
 
 	ex->dw.data = ex;
 	ex->dw.x = x - (int)(explosion_sprite.cellw / 2);
@@ -122,29 +122,29 @@
 	ex->dw.draw = explosion_draw;
 	ex->dw.finish = explosion_finish;
 
-	drawable_stack_add(&stack, &ex->dw);
+	mlk_drawable_stack_add(&stack, &ex->dw);
 }
 
 static void
-handle(struct state *st, const union event *ev)
+handle(struct state *st, const union mlk_event *ev)
 {
 	(void)st;
 
 	switch (ev->type) {
-	case EVENT_KEYDOWN:
+	case MLK_EVENT_KEYDOWN:
 		switch (ev->key.key) {
-		case KEY_ESCAPE:
-			drawable_stack_finish(&stack);
+		case MLK_KEY_ESCAPE:
+			mlk_drawable_stack_finish(&stack);
 			break;
 		default:
 			break;
 		}
 		break;
-	case EVENT_CLICKDOWN:
+	case MLK_EVENT_CLICKDOWN:
 		spawn(ev->click.x, ev->click.y);
 		break;
-	case EVENT_QUIT:
-		game_quit();
+	case MLK_EVENT_QUIT:
+		mlk_game_quit();
 		break;
 	default:
 		break;
@@ -156,7 +156,7 @@
 {
 	(void)st;
 
-	drawable_stack_update(&stack, ticks);
+	mlk_drawable_stack_update(&stack, ticks);
 
 }
 
@@ -165,11 +165,11 @@
 {
 	(void)st;
 
-	painter_set_color(0xebede9ff);
-	painter_clear();
+	mlk_painter_set_color(0xebede9ff);
+	mlk_painter_clear();
 	label_draw(&help);
-	drawable_stack_draw(&stack);
-	painter_present();
+	mlk_drawable_stack_draw(&stack);
+	mlk_painter_present();
 }
 
 static void
@@ -181,11 +181,11 @@
 		.draw = draw
 	};
 
-	drawable_stack_init(&stack, drawables, UTIL_SIZE(drawables));
+	mlk_drawable_stack_init(&stack, drawables, UTIL_SIZE(drawables));
 
-	game_init(states, UTIL_SIZE(states));
-	game_push(&state);
-	game_loop();
+	mlk_game_init(states, UTIL_SIZE(states));
+	mlk_game_push(&state);
+	mlk_game_loop();
 }
 
 static void
--- a/examples/example-font/CMakeLists.txt	Fri Feb 24 22:14:42 2023 +0100
+++ b/examples/example-font/CMakeLists.txt	Sun Feb 26 19:44:47 2023 +0100
@@ -20,13 +20,13 @@
 
 set(
 	SOURCES
-	${example-font_SOURCE_DIR}/main.c
+	${example-font_SOURCE_DIR}/example-font.c
 )
 
 mlk_executable(
 	NAME example-font
 	FOLDER examples
-	LIBRARIES libmlk-ui
+	LIBRARIES libmlk-example
 	SOURCES ${SOURCES}
 )
 
--- a/examples/example-font/example-font.c	Fri Feb 24 22:14:42 2023 +0100
+++ b/examples/example-font/example-font.c	Sun Feb 26 19:44:47 2023 +0100
@@ -48,7 +48,7 @@
 
 static struct state *states[1];
 static int ci = 0;
-static enum font_style style = FONT_STYLE_ANTIALIASED;
+static enum mlk_font_style style = MLK_FONT_STYLE_ANTIALIASED;
 
 static void
 init(void)
@@ -60,34 +60,34 @@
 }
 
 static void
-handle(struct state *st, const union event *ev)
+handle(struct state *st, const union mlk_event *ev)
 {
 	(void)st;
 
 	switch (ev->type) {
-	case EVENT_KEYDOWN:
+	case MLK_EVENT_KEYDOWN:
 		switch (ev->key.key) {
-		case KEY_LEFT:
+		case MLK_KEY_LEFT:
 			if (ci > 0)
 				ci--;
 			break;
-		case KEY_RIGHT:
+		case MLK_KEY_RIGHT:
 			if ((size_t)ci < UTIL_SIZE(colors))
 				ci++;
 			break;
-		case KEY_SPACE:
-			if (style == FONT_STYLE_ANTIALIASED)
-				style = FONT_STYLE_NONE;
+		case MLK_KEY_SPACE:
+			if (style == MLK_FONT_STYLE_ANTIALIASED)
+				style = MLK_FONT_STYLE_NONE;
 			else
-				style = FONT_STYLE_ANTIALIASED;
+				style = MLK_FONT_STYLE_ANTIALIASED;
 
 			theme_default()->fonts[THEME_FONT_INTERFACE]->style = style;
 		default:
 			break;
 		}
 		break;
-	case EVENT_QUIT:
-		game_quit();
+	case MLK_EVENT_QUIT:
+		mlk_game_quit();
 		break;
 	default:
 		break;
@@ -99,17 +99,17 @@
 {
 	(void)st;
 
-	struct font *font = theme_default()->fonts[THEME_FONT_INTERFACE];
+	struct mlk_font *font = theme_default()->fonts[THEME_FONT_INTERFACE];
 	struct texture tex;
 
-	painter_set_color(0xffffffff);
-	painter_clear();
+	mlk_painter_set_color(0xffffffff);
+	mlk_painter_clear();
 
-	if (font_render(font, &tex, "Example of text. Use <Left>/<Right> to change color and <Space> to toggle antialiasing.", colors[ci]) < 0)
+	if (mlk_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();
+	mlk_painter_present();
 	texture_finish(&tex);
 }
 
@@ -121,9 +121,9 @@
 		.draw = draw
 	};
 
-	game_init(states, UTIL_SIZE(states));
-	game_push(&state);
-	game_loop();
+	mlk_game_init(states, UTIL_SIZE(states));
+	mlk_game_push(&state);
+	mlk_game_loop();
 }
 
 static void
--- a/examples/example-gridmenu/CMakeLists.txt	Fri Feb 24 22:14:42 2023 +0100
+++ b/examples/example-gridmenu/CMakeLists.txt	Sun Feb 26 19:44:47 2023 +0100
@@ -20,13 +20,13 @@
 
 set(
 	SOURCES
-	${example-gridmenu_SOURCE_DIR}/main.c
+	${example-gridmenu_SOURCE_DIR}/example-gridmenu.c
 )
 
 mlk_executable(
 	NAME example-gridmenu
 	FOLDER examples
-	LIBRARIES libmlk-ui
+	LIBRARIES libmlk-example
 	SOURCES ${SOURCES}
 )
 
--- a/examples/example-gridmenu/example-gridmenu.c	Fri Feb 24 22:14:42 2023 +0100
+++ b/examples/example-gridmenu/example-gridmenu.c	Sun Feb 26 19:44:47 2023 +0100
@@ -56,13 +56,13 @@
 }
 
 static void
-handle(struct state *st, const union event *ev)
+handle(struct state *st, const union mlk_event *ev)
 {
 	struct gridmenu *menu = st->data;
 
 	switch (ev->type) {
-	case EVENT_QUIT:
-		game_quit();
+	case MLK_EVENT_QUIT:
+		mlk_game_quit();
 		break;
 	default:
 		if (gridmenu_handle(st->data, ev))
@@ -74,10 +74,10 @@
 static void
 draw(struct state *st)
 {
-	painter_set_color(0x4f8fbaff);
-	painter_clear();
+	mlk_painter_set_color(0x4f8fbaff);
+	mlk_painter_clear();
 	gridmenu_draw(st->data);
-	painter_present();
+	mlk_painter_present();
 }
 
 static void
@@ -115,9 +115,9 @@
 
 	align(ALIGN_CENTER, &menu.x, &menu.y, menu.w, menu.h, 0, 0, W, H);
 
-	game_init(states, UTIL_SIZE(states));
-	game_push(&state);
-	game_loop();
+	mlk_game_init(states, UTIL_SIZE(states));
+	mlk_game_push(&state);
+	mlk_game_loop();
 }
 
 int
--- a/examples/example-label/CMakeLists.txt	Fri Feb 24 22:14:42 2023 +0100
+++ b/examples/example-label/CMakeLists.txt	Sun Feb 26 19:44:47 2023 +0100
@@ -20,13 +20,13 @@
 
 set(
 	SOURCES
-	${example-label_SOURCE_DIR}/main.c
+	${example-label_SOURCE_DIR}/example-label.c
 )
 
 mlk_executable(
 	NAME example-label
 	FOLDER examples
-	LIBRARIES libmlk-ui
+	LIBRARIES libmlk-example
 	SOURCES ${SOURCES}
 )
 
--- a/examples/example-label/example-label.c	Fri Feb 24 22:14:42 2023 +0100
+++ b/examples/example-label/example-label.c	Sun Feb 26 19:44:47 2023 +0100
@@ -129,17 +129,17 @@
 }
 
 static void
-handle(struct state *st, const union event *ev)
+handle(struct state *st, const union mlk_event *ev)
 {
 	(void)st;
 
 	switch (ev->type) {
-	case EVENT_MOUSE:
+	case MLK_EVENT_MOUSE:
 		mlabel.x = ev->mouse.x;
 		mlabel.y = ev->mouse.y;
 		break;
-	case EVENT_QUIT:
-		game_quit();
+	case MLK_EVENT_QUIT:
+		mlk_game_quit();
 		break;
 	default:
 		break;
@@ -151,14 +151,14 @@
 {
 	(void)st;
 
-	painter_set_color(0x4f8fbaff);
-	painter_clear();
+	mlk_painter_set_color(0x4f8fbaff);
+	mlk_painter_clear();
 
 	for (size_t i = 0; i < UTIL_SIZE(table); ++i)
 		label_draw(&table[i].label);
 
 	label_draw(&mlabel);
-	painter_present();
+	mlk_painter_present();
 }
 
 static void
@@ -169,9 +169,9 @@
 		.draw = draw
 	};
 
-	game_init(states, UTIL_SIZE(states));
-	game_push(&state);
-	game_loop();
+	mlk_game_init(states, UTIL_SIZE(states));
+	mlk_game_push(&state);
+	mlk_game_loop();
 }
 
 int
--- a/examples/example-message/CMakeLists.txt	Fri Feb 24 22:14:42 2023 +0100
+++ b/examples/example-message/CMakeLists.txt	Sun Feb 26 19:44:47 2023 +0100
@@ -20,13 +20,13 @@
 
 set(
 	SOURCES
-	${example-message_SOURCE_DIR}/main.c
+	${example-message_SOURCE_DIR}/example-message.c
 )
 
 mlk_executable(
 	NAME example-message
 	FOLDER examples
-	LIBRARIES libmlk-rpg
+	LIBRARIES libmlk-example
 	SOURCES ${SOURCES}
 )
 
--- a/examples/example-message/example-message.c	Fri Feb 24 22:14:42 2023 +0100
+++ b/examples/example-message/example-message.c	Sun Feb 26 19:44:47 2023 +0100
@@ -64,11 +64,11 @@
 }
 
 static void
-handle(struct state *st, const union event *ev)
+handle(struct state *st, const union mlk_event *ev)
 {
 	switch (ev->type) {
-	case EVENT_QUIT:
-		game_quit();
+	case MLK_EVENT_QUIT:
+		mlk_game_quit();
 		break;
 	default:
 		message_handle(st->data, ev);
@@ -80,16 +80,16 @@
 update(struct state *st, unsigned int ticks)
 {
 	if (message_update(st->data, ticks))
-		game_quit();
+		mlk_game_quit();
 }
 
 static void
 draw(struct state *st)
 {
-	painter_set_color(0xffffffff);
-	painter_clear();
+	mlk_painter_set_color(0xffffffff);
+	mlk_painter_clear();
 	message_draw(st->data);
-	painter_present();
+	mlk_painter_present();
 }
 
 static void
@@ -104,9 +104,9 @@
 
 	message_start(msg);
 
-	game_init(states, UTIL_SIZE(states));
-	game_push(&state);
-	game_loop();
+	mlk_game_init(states, UTIL_SIZE(states));
+	mlk_game_push(&state);
+	mlk_game_loop();
 }
 
 static void
@@ -114,8 +114,8 @@
 {
 	(void)th;
 
-	painter_set_color(0xff0000ff);
-	painter_draw_rectangle(f->x, f->y, f->w, f->h);
+	mlk_painter_set_color(0xff0000ff);
+	mlk_painter_draw_rectangle(f->x, f->y, f->w, f->h);
 }
 
 static void
--- a/examples/example-notify/CMakeLists.txt	Fri Feb 24 22:14:42 2023 +0100
+++ b/examples/example-notify/CMakeLists.txt	Sun Feb 26 19:44:47 2023 +0100
@@ -20,20 +20,14 @@
 
 set(
 	SOURCES
-	${example-notify_SOURCE_DIR}/main.c
-)
-
-set(
-	ASSETS
-	${example-notify_SOURCE_DIR}/assets/images/sword.png
+	${example-notify_SOURCE_DIR}/example-notify.c
 )
 
 mlk_executable(
 	NAME example-notify
 	FOLDER examples
-	LIBRARIES libmlk-rpg
-	ASSETS ${ASSETS}
-	SOURCES ${ASSETS} ${SOURCES}
+	LIBRARIES libmlk-example
+	SOURCES ${SOURCES}
 )
 
-source_group(TREE ${example-notify_SOURCE_DIR} FILES ${ASSETS} ${SOURCES})
+source_group(TREE ${example-notify_SOURCE_DIR} FILES ${SOURCES})
--- a/examples/example-notify/example-notify.c	Fri Feb 24 22:14:42 2023 +0100
+++ b/examples/example-notify/example-notify.c	Sun Feb 26 19:44:47 2023 +0100
@@ -55,21 +55,21 @@
 		panic();
 	if (window_open("Example - Notify", W, H) < 0)
 		panic();
-	if (image_openmem(&icon, assets_sword, sizeof (assets_sword)) < 0)
+	if (mlk_image_openmem(&icon, assets_images_sword, sizeof (assets_images_sword)) < 0)
 		panic();
 }
 
 static void
-handle(struct state *st, const union event *ev)
+handle(struct state *st, const union mlk_event *ev)
 {
 	(void)st;
 
 	switch (ev->type) {
-	case EVENT_QUIT:
-		game_quit();
+	case MLK_EVENT_QUIT:
+		mlk_game_quit();
 		break;
-	case EVENT_KEYDOWN:
-		if (ev->key.key == KEY_SPACE)
+	case MLK_EVENT_KEYDOWN:
+		if (ev->key.key == MLK_KEY_SPACE)
 			notify(&icon, "Quest", "Quest finished.");
 		break;
 	default:
@@ -90,11 +90,11 @@
 {
 	(void)st;
 
-	painter_set_color(0xffffffff);
-	painter_clear();
+	mlk_painter_set_color(0xffffffff);
+	mlk_painter_clear();
 	label_draw(&help);
 	notify_draw();
-	painter_present();
+	mlk_painter_present();
 }
 
 static void
@@ -106,9 +106,9 @@
 		.draw = draw
 	};
 
-	game_init(states, UTIL_SIZE(states));
-	game_push(&state);
-	game_loop();
+	mlk_game_init(states, UTIL_SIZE(states));
+	mlk_game_push(&state);
+	mlk_game_loop();
 }
 
 static void
--- a/examples/example-sprite/CMakeLists.txt	Fri Feb 24 22:14:42 2023 +0100
+++ b/examples/example-sprite/CMakeLists.txt	Sun Feb 26 19:44:47 2023 +0100
@@ -20,20 +20,14 @@
 
 set(
 	SOURCES
-	${example-sprite_SOURCE_DIR}/main.c
-)
-
-set(
-	ASSETS
-	${example-sprite_SOURCE_DIR}/assets/sprites/people.png
+	${example-sprite_SOURCE_DIR}/example-sprite.c
 )
 
 mlk_executable(
 	NAME example-sprite
 	FOLDER examples
-	LIBRARIES libmlk-ui
-	SOURCES ${ASSETS} ${SOURCES}
-	ASSETS ${ASSETS}
+	LIBRARIES libmlk-example
+	SOURCES ${SOURCES}
 )
 
-source_group(TREE ${example-sprite_SOURCE_DIR} FILES ${ASSETS} ${SOURCES})
+source_group(TREE ${example-sprite_SOURCE_DIR} FILES ${SOURCES})
--- a/examples/example-sprite/example-sprite.c	Fri Feb 24 22:14:42 2023 +0100
+++ b/examples/example-sprite/example-sprite.c	Sun Feb 26 19:44:47 2023 +0100
@@ -69,33 +69,33 @@
 		panic();
 	if (window_open("Example - Sprite", W, H) < 0)
 		panic();
-	if (image_openmem(&texture, assets_people, sizeof (assets_people)) < 0)
+	if (mlk_image_openmem(&texture, assets_sprites_people, sizeof (assets_sprites_people)) < 0)
 		panic();
 
 	sprite_init(&sprite, &texture, 48, 48);
 }
 
 static void
-handle(struct state *st, const union event *ev)
+handle(struct state *st, const union mlk_event *ev)
 {
 	(void)st;
 
 	switch (ev->type) {
-	case EVENT_KEYDOWN:
+	case MLK_EVENT_KEYDOWN:
 		switch (ev->key.key) {
-		case KEY_LEFT:
+		case MLK_KEY_LEFT:
 			if (column > 0)
 				column--;
 			break;
-		case KEY_RIGHT:
+		case MLK_KEY_RIGHT:
 			if (column + 1 < sprite.ncols)
 				column++;
 			break;
-		case KEY_UP:
+		case MLK_KEY_UP:
 			if (row > 0)
 				row--;
 			break;
-		case KEY_DOWN:
+		case MLK_KEY_DOWN:
 			if (row + 1 < sprite.nrows)
 				row++;
 			break;
@@ -105,8 +105,8 @@
 
 		changed();
 		break;
-	case EVENT_QUIT:
-		game_quit();
+	case MLK_EVENT_QUIT:
+		mlk_game_quit();
 		break;
 	default:
 		break;
@@ -120,12 +120,12 @@
 
 	int x, y;
 
-	painter_set_color(0xebede9ff);
-	painter_clear();
+	mlk_painter_set_color(0xebede9ff);
+	mlk_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();
+	mlk_painter_present();
 }
 
 static void
@@ -138,9 +138,9 @@
 
 	changed();
 
-	game_init(states, UTIL_SIZE(states));
-	game_push(&state);
-	game_loop();
+	mlk_game_init(states, UTIL_SIZE(states));
+	mlk_game_push(&state);
+	mlk_game_loop();
 }
 
 static void
--- a/examples/example-trace/CMakeLists.txt	Fri Feb 24 22:14:42 2023 +0100
+++ b/examples/example-trace/CMakeLists.txt	Sun Feb 26 19:44:47 2023 +0100
@@ -20,14 +20,13 @@
 
 set(
 	SOURCES
-	${example-trace_SOURCE_DIR}/main.c
-	${example-trace_SOURCE_DIR}/trace_hud.c
+	${example-trace_SOURCE_DIR}/example-trace.c
 )
 
 mlk_executable(
 	NAME example-trace
 	FOLDER examples
-	LIBRARIES libmlk-rpg
+	LIBRARIES libmlk-example
 	SOURCES ${SOURCES}
 )
 
--- a/examples/example-trace/example-trace.c	Fri Feb 24 22:14:42 2023 +0100
+++ b/examples/example-trace/example-trace.c	Sun Feb 26 19:44:47 2023 +0100
@@ -16,21 +16,21 @@
  * 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 <mlk/core/core.h>
+#include <mlk/core/event.h>
+#include <mlk/core/game.h>
+#include <mlk/core/sys.h>
+#include <mlk/core/window.h>
+#include <mlk/core/painter.h>
+#include <mlk/core/panic.h>
+#include <mlk/core/state.h>
+#include <mlk/core/trace.h>
+#include <mlk/core/util.h>
 
-#include <ui/theme.h>
-#include <ui/ui.h>
+#include <mlk/ui/theme.h>
+#include <mlk/ui/ui.h>
 
-#include "trace_hud.h"
+#include <mlk/example/trace-hud.h>
 
 #define W 1280
 #define H 720
@@ -49,14 +49,14 @@
 }
 
 static void
-handle(struct state *st, const union event *ev)
+handle(struct state *st, const union mlk_event *ev)
 {
 	(void)st;
 
 	switch (ev->type) {
-	case EVENT_KEYDOWN:
+	case MLK_EVENT_KEYDOWN:
 		switch (ev->key.key) {
-		case KEY_ESCAPE:
+		case MLK_KEY_ESCAPE:
 			trace_hud_clear();
 			break;
 		default:
@@ -64,11 +64,11 @@
 			break;
 		}
 		break;
-	case EVENT_CLICKDOWN:
+	case MLK_EVENT_CLICKDOWN:
 		tracef("click at %d,%d", ev->click.x, ev->click.y);
 		break;
-	case EVENT_QUIT:
-		game_quit();
+	case MLK_EVENT_QUIT:
+		mlk_game_quit();
 		break;
 	default:
 		break;
@@ -88,10 +88,10 @@
 {
 	(void)st;
 
-	painter_set_color(0x4f8fbaff);
-	painter_clear();
+	mlk_painter_set_color(0x4f8fbaff);
+	mlk_painter_clear();
 	trace_hud_draw();
-	painter_present();
+	mlk_painter_present();
 }
 
 static void
@@ -103,9 +103,9 @@
 		.draw = draw
 	};
 
-	game_init(states, UTIL_SIZE(states));
-	game_push(&state);
-	game_loop();
+	mlk_game_init(states, UTIL_SIZE(states));
+	mlk_game_push(&state);
+	mlk_game_loop();
 }
 
 static void
--- a/examples/example-ui/CMakeLists.txt	Fri Feb 24 22:14:42 2023 +0100
+++ b/examples/example-ui/CMakeLists.txt	Sun Feb 26 19:44:47 2023 +0100
@@ -20,13 +20,13 @@
 
 set(
 	SOURCES
-	${example-ui_SOURCE_DIR}/main.c
+	${example-ui_SOURCE_DIR}/example-ui.c
 )
 
 mlk_executable(
 	NAME example-ui
 	FOLDER examples
-	LIBRARIES libmlk-ui
+	LIBRARIES libmlk-example
 	SOURCES ${SOURCES}
 )
 
--- a/examples/example-ui/example-ui.c	Fri Feb 24 22:14:42 2023 +0100
+++ b/examples/example-ui/example-ui.c	Sun Feb 26 19:44:47 2023 +0100
@@ -186,7 +186,7 @@
 static int
 headerclick(int x, int y)
 {
-	return maths_is_boxed(
+	return mlk_maths_is_boxed(
 	    ui.panel.frame.x,
 	    ui.panel.frame.y,
 	    ui.panel.frame.w,
@@ -197,15 +197,15 @@
 }
 
 static void
-handle(struct state *st, const union event *ev)
+handle(struct state *st, const union mlk_event *ev)
 {
 	(void)st;
 
 	switch (ev->type) {
-	case EVENT_QUIT:
-		game_quit();
+	case MLK_EVENT_QUIT:
+		mlk_game_quit();
 		break;
-	case EVENT_MOUSE:
+	case MLK_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;
@@ -214,7 +214,7 @@
 			resize();
 		}
 		break;
-	case EVENT_CLICKDOWN:
+	case MLK_EVENT_CLICKDOWN:
 		if (headerclick(ev->click.x, ev->click.y)) {
 			ui.motion.active = 1;
 			ui.motion.x = ev->click.x;
@@ -222,7 +222,7 @@
 			window_set_cursor(WINDOW_CURSOR_SIZE);
 		}
 		break;
-	case EVENT_CLICKUP:
+	case MLK_EVENT_CLICKUP:
 		ui.motion.active = 0;
 		window_set_cursor(WINDOW_CURSOR_ARROW);
 		break;
@@ -233,7 +233,7 @@
 	checkbox_handle(&ui.autosave.cb, ev);
 
 	if (button_handle(&ui.quit.button, ev))
-		game_quit();
+		mlk_game_quit();
 }
 
 static void
@@ -241,14 +241,14 @@
 {
 	(void)st;
 
-	painter_set_color(0xffffffff);
-	painter_clear();
+	mlk_painter_set_color(0xffffffff);
+	mlk_painter_clear();
 	frame_draw(&ui.panel.frame);
 	label_draw(&ui.header.label);
 	checkbox_draw(&ui.autosave.cb);
 	label_draw(&ui.autosave.label);
 	button_draw(&ui.quit.button);
-	painter_present();
+	mlk_painter_present();
 }
 
 static void
@@ -261,9 +261,9 @@
 
 	resize();
 
-	game_init(states, UTIL_SIZE(states));
-	game_push(&state);
-	game_loop();
+	mlk_game_init(states, UTIL_SIZE(states));
+	mlk_game_push(&state);
+	mlk_game_loop();
 }
 
 static void
--- a/examples/example/character-john.c	Fri Feb 24 22:14:42 2023 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,50 +0,0 @@
-/*
- * character-john.c -- john character
- *
- * Copyright (c) 2020-2023 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER 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 <mlk/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 ",
-	.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
-	}
-};
--- a/examples/example/character-john.h	Fri Feb 24 22:14:42 2023 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,24 +0,0 @@
-/*
- * character-john.h -- john character
- *
- * Copyright (c) 2020-2023 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER 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/examples/example/registry.c	Fri Feb 24 22:14:42 2023 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,140 +0,0 @@
-/*
- * registry.h -- registry of resources
- *
- * Copyright (c) 2020-2023 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER 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 <mlk/core/image.h>
-#include <mlk/core/panic.h>
-#include <mlk/core/util.h>
-#include <mlk/core/sys.h>
-
-#include <assets/images/battle-background.h>
-#include <assets/images/black-cat.h>
-#include <assets/images/haunted-wood.h>
-
-#include <assets/sounds/fire.h>
-
-#include <assets/sprites/explosion.h>
-#include <assets/sprites/john-sword.h>
-#include <assets/sprites/john-walk.h>
-#include <assets/sprites/ui-cursor.h>
-
-#include "registry.h"
-
-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, data) \
-	{ (i), (data), sizeof (data) }
-
-static const struct {
-	enum registry_image index;
-	const unsigned char *data;
-	size_t datasz;
-} images[] = {
-	REGISTRY_IMAGE(REGISTRY_IMAGE_BATTLE_BACKGROUND, assets_battle_background)
-};
-
-#define REGISTRY_TEXTURE(s, data, cw, ch) \
-	{ (s), (data), sizeof (data), (cw), (ch) }
-
-static const struct {
-	enum registry_texture index;
-	const unsigned char *data;
-	size_t datasz;
-	unsigned int cellw;
-	unsigned int cellh;
-} textures[] = {
-	REGISTRY_TEXTURE(REGISTRY_TEXTURE_CURSOR, assets_ui_cursor, 24, 24),
-	REGISTRY_TEXTURE(REGISTRY_TEXTURE_EXPLOSION, assets_explosion, 256, 256),
-	REGISTRY_TEXTURE(REGISTRY_TEXTURE_JOHN_SWORD, assets_john_sword, 256, 256),
-	REGISTRY_TEXTURE(REGISTRY_TEXTURE_JOHN_WALK, assets_john_walk, 256, 256),
-	REGISTRY_TEXTURE(REGISTRY_TEXTURE_HAUNTED_WOOD, assets_haunted_wood, 0, 0),
-	REGISTRY_TEXTURE(REGISTRY_TEXTURE_BLACK_CAT, assets_black_cat, 0, 0)
-};
-
-#define REGISTRY_SOUND(s, data) \
-	{ (s), (data), sizeof (data) }
-
-static const struct {
-	enum registry_sound index;
-	const unsigned char *data;
-	size_t datasz;
-} sounds[] = {
-	REGISTRY_SOUND(REGISTRY_SOUND_FIRE, assets_fire)
-};
-
-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_openmem(texture, images[i].data, images[i].datasz) < 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_openmem(texture, textures[i].data, textures[i].datasz) < 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_openmem(sound, sounds[i].data, sounds[i].datasz) < 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/examples/example/registry.h	Fri Feb 24 22:14:42 2023 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,66 +0,0 @@
-/*
- * registry.h -- registry of resources
- *
- * Copyright (c) 2020-2023 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER 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 <mlk/core/sound.h>
-#include <mlk/core/sprite.h>
-#include <mlk/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/examples/example/spell-fire.c	Fri Feb 24 22:14:42 2023 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,132 +0,0 @@
-/*
- * spell-fire.c -- example of spell: fire
- *
- * Copyright (c) 2020-2023 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER 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 <mlk/core/action.h>
-#include <mlk/core/animation.h>
-#include <mlk/core/drawable.h>
-#include <mlk/core/alloc.h>
-
-#include <mlk/ui/align.h>
-
-#include <mlk/rpg/battle-state-check.h>
-#include <mlk/rpg/battle-state-rendering.h>
-#include <mlk/rpg/battle.h>
-#include <mlk/rpg/character.h>
-#include <mlk/rpg/spell.h>
-
-#include "registry.h"
-#include "spell-fire.h"
-
-struct self {
-	struct battle *battle;
-	struct character *target;
-	struct animation animation;
-	struct drawable drawable;
-	unsigned int selection;
-};
-
-static int
-update(struct drawable *dw, unsigned int ticks)
-{
-	struct self *self = dw->data;
-
-	return animation_update(&self->animation, ticks);
-}
-
-static void
-draw(struct drawable *dw)
-{
-	const struct self *self = dw->data;
-	const struct battle_entity *et = self->battle->enemies[self->selection];
-	const struct sprite *sprite = et->ch->sprites[CHARACTER_SPRITE_NORMAL];
-	int x, y;
-
-	align(ALIGN_CENTER,
-	    &x, &y, self->animation.sprite->cellw, self->animation.sprite->cellh,
-	    et->x, et->y, sprite->cellw, sprite->cellh);
-
-	animation_draw(&self->animation, x, y);
-}
-
-static void
-end(struct drawable *dw)
-{
-	struct self *self = dw->data;
-	struct character *ch = self->battle->enemies[self->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(self->battle, self->battle->enemies[self->selection]->ch, 100);
-	battle_state_check(self->battle);
-}
-
-static void
-finish(struct drawable *dw)
-{
-	free(dw->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 self *self;
-
-	(void)owner;
-
-	self = mlk_alloc_new0(sizeof (*self));
-	self->selection = slt->index_character;
-	self->battle = bt;
-	self->drawable.data = self;
-	self->drawable.update = update;
-	self->drawable.draw = draw;
-	self->drawable.finish = finish;
-	self->drawable.end = end;
-
-	animation_init(&self->animation, &registry_sprites[REGISTRY_TEXTURE_EXPLOSION], 12);
-	animation_start(&self->animation);
-
-	sound_play(&registry_sounds[REGISTRY_SOUND_FIRE]);
-	battle_state_rendering(bt, &self->drawable);
-}
-
-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/examples/example/spell-fire.h	Fri Feb 24 22:14:42 2023 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,24 +0,0 @@
-/*
- * spell-fire.c -- example of spell: fire
- *
- * Copyright (c) 2020-2023 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER 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/examples/example/trace_hud.c	Fri Feb 24 22:14:42 2023 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,145 +0,0 @@
-/*
- * trace_hud.c -- on screen hud
- *
- * Copyright (c) 2020-2023 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <assert.h>
-#include <stdio.h>
-#include <string.h>
-
-#include <mlk/core/action.h>
-#include <mlk/core/font.h>
-#include <mlk/core/trace.h>
-#include <mlk/core/window.h>
-
-#include <mlk/ui/label.h>
-#include <mlk/ui/theme.h>
-
-#include "trace_hud.h"
-
-#define LINES_MAX       (4)
-#define THEME(t)        ((t) ? (t) : theme_default())
-
-static struct {
-	char lines[LINES_MAX + 1][TRACE_LINE_MAX];
-	unsigned int elapsed;
-} data;
-
-struct trace_hud trace_hud = {
-	.timeout = TRACE_HUD_TIMEOUT_DEFAULT
-};
-
-void
-trace_hud_handler(const char *str)
-{
-	assert(str);
-
-	/* 1.Try to find an empty line. */
-	for (size_t i = 0; i < LINES_MAX; ++i) {
-		if (data.lines[i][0] == '\0') {
-			snprintf(data.lines[i], sizeof (data.lines[i]), "%s", str);
-			return;
-		}
-	}
-
-	/* 2. All lines are full, put in last one and move other. */
-	memmove(&data.lines[0], &data.lines[1], sizeof (data.lines[0]) * LINES_MAX - 1);
-	snprintf(data.lines[LINES_MAX - 1], sizeof (data.lines[0]), "%s", str);
-
-	/* 3. Reset elapsed time now. */
-	data.elapsed = 0;
-}
-
-void
-trace_hud_update(unsigned int ticks)
-{
-	data.elapsed += ticks;
-
-	/*
-	 * We have an empty line in the data.lines at LINES_MAX, so we simply so
-	 * to move the whole array.
-	 *
-	 * [0] = "abc"
-	 * [1] = "def"
-	 * [2] = "xyz"
-	 * [3] = "zef"
-	 * [n] = "ldkf"
-	 * [LINES_MAX + 1] = "\0"
-	 */
-	if (data.elapsed >= trace_hud.timeout) {
-		data.elapsed = 0;
-		memmove(&data.lines[0], &data.lines[1], sizeof (data.lines[0]) * LINES_MAX);
-	}
-}
-
-void
-trace_hud_draw(void)
-{
-	struct theme *th;
-	int x, y;
-
-	th = THEME(trace_hud.theme);
-	x = th->padding;
-	y = th->padding;
-
-	for (int i = 0; i < LINES_MAX && data.lines[i][0]; ++i) {
-		label_draw(&(struct label) {
-			.x = x,
-			.y = y,
-			.text = data.lines[i],
-			.theme = th,
-			.flags = LABEL_FLAGS_SHADOW
-		});
-
-		y += font_height(th->fonts[THEME_FONT_INTERFACE]);
-		y += th->padding;
-	}
-}
-
-void
-trace_hud_clear(void)
-{
-	memset(&data, 0, sizeof (data));
-}
-
-static int
-update(struct mlk_action *a, unsigned int ticks)
-{
-	(void)a;
-
-	trace_hud_update(ticks);
-
-	return 0;
-}
-
-static void
-draw(struct mlk_action *a)
-{
-	(void)a;
-
-	trace_hud_draw();
-}
-
-struct mlk_action *
-trace_hud_action(void)
-{
-	static struct mlk_action a = {
-		.update = update,
-		.draw = draw
-	};
-
-	return &a;
-}
--- a/examples/example/trace_hud.h	Fri Feb 24 22:14:42 2023 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,49 +0,0 @@
-/*
- * trace_hud.h -- on screen hud
- *
- * Copyright (c) 2020-2023 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER 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_TRACE_TRACE_HUD_H
-#define EXAMPLES_TRACE_TRACE_HUD_H
-
-#define TRACE_HUD_TIMEOUT_DEFAULT (3000)
-
-struct action;
-struct theme;
-
-struct trace_hud {
-	struct theme *theme;
-	unsigned int timeout;
-};
-
-extern struct trace_hud trace_hud;
-
-void
-trace_hud_handler(const char *);
-
-void
-trace_hud_update(unsigned int);
-
-void
-trace_hud_draw(void);
-
-void
-trace_hud_clear(void);
-
-struct mlk_action *
-trace_hud_action(void);
-
-#endif /* !EXAMPLES_TRACE_TRACE_HUD_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-example/CMakeLists.txt	Sun Feb 26 19:44:47 2023 +0100
@@ -0,0 +1,62 @@
+#
+# CMakeLists.txt -- CMake build system for Molko's Engine
+#
+# Copyright (c) 2020-2022 David Demelier <markand@malikania.fr>
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+#
+
+project(libmlk-example)
+
+set(
+	SOURCES
+	${libmlk-example_SOURCE_DIR}/mlk/example/character-john.c
+	${libmlk-example_SOURCE_DIR}/mlk/example/character-john.h
+	${libmlk-example_SOURCE_DIR}/mlk/example/registry.c
+	${libmlk-example_SOURCE_DIR}/mlk/example/registry.h
+	${libmlk-example_SOURCE_DIR}/mlk/example/spell-fire.c
+	${libmlk-example_SOURCE_DIR}/mlk/example/spell-fire.h
+	${libmlk-example_SOURCE_DIR}/mlk/example/trace-hud.c
+	${libmlk-example_SOURCE_DIR}/mlk/example/trace-hud.h
+)
+
+set(
+	ASSETS
+	${libmlk-example_SOURCE_DIR}/assets/images/battle-background.png
+	${libmlk-example_SOURCE_DIR}/assets/images/black-cat.png
+	${libmlk-example_SOURCE_DIR}/assets/images/haunted-wood.png
+	${libmlk-example_SOURCE_DIR}/assets/images/sword.png
+	${libmlk-example_SOURCE_DIR}/assets/music/vabsounds-romance.ogg
+	${libmlk-example_SOURCE_DIR}/assets/sounds/fire.wav
+	${libmlk-example_SOURCE_DIR}/assets/sprites/chest.png
+	${libmlk-example_SOURCE_DIR}/assets/sprites/explosion.png
+	${libmlk-example_SOURCE_DIR}/assets/sprites/john-sword.png
+	${libmlk-example_SOURCE_DIR}/assets/sprites/john-walk.png
+	${libmlk-example_SOURCE_DIR}/assets/sprites/numbers.png
+	${libmlk-example_SOURCE_DIR}/assets/sprites/people.png
+	${libmlk-example_SOURCE_DIR}/assets/sprites/ui-cursor.png
+)
+
+mlk_library(
+	NAME libmlk-example
+	SOURCES ${SOURCES}
+	ASSETS ${ASSETS}
+	TYPE STATIC
+	LIBRARIES libmlk-rpg
+	INCLUDES
+		PUBLIC
+			$<BUILD_INTERFACE:${libmlk-example_SOURCE_DIR}>
+			$<BUILD_INTERFACE:${libmlk-example_BINARY_DIR}>
+)
+
+source_group(TREE ${libmlk-example_SOURCE_DIR} FILES ${ASSETS} ${SOURCES})
Binary file libmlk-example/assets/images/battle-background.png has changed
Binary file libmlk-example/assets/images/black-cat.png has changed
Binary file libmlk-example/assets/images/haunted-wood.png has changed
Binary file libmlk-example/assets/images/sword.png has changed
Binary file libmlk-example/assets/music/vabsounds-romance.ogg has changed
Binary file libmlk-example/assets/sounds/fire.wav has changed
Binary file libmlk-example/assets/sprites/chest.png has changed
Binary file libmlk-example/assets/sprites/explosion.png has changed
Binary file libmlk-example/assets/sprites/john-sword.png has changed
Binary file libmlk-example/assets/sprites/john-walk.png has changed
Binary file libmlk-example/assets/sprites/numbers.png has changed
Binary file libmlk-example/assets/sprites/people.png has changed
Binary file libmlk-example/assets/sprites/ui-cursor.png has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-example/mlk/example/character-john.c	Sun Feb 26 19:44:47 2023 +0100
@@ -0,0 +1,50 @@
+/*
+ * character-john.c -- john character
+ *
+ * Copyright (c) 2020-2023 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER 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 <mlk/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 ",
+	.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
+	}
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-example/mlk/example/character-john.h	Sun Feb 26 19:44:47 2023 +0100
@@ -0,0 +1,24 @@
+/*
+ * character-john.h -- john character
+ *
+ * Copyright (c) 2020-2023 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER 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/libmlk-example/mlk/example/registry.c	Sun Feb 26 19:44:47 2023 +0100
@@ -0,0 +1,140 @@
+/*
+ * registry.h -- registry of resources
+ *
+ * Copyright (c) 2020-2023 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER 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 <mlk/core/image.h>
+#include <mlk/core/panic.h>
+#include <mlk/core/util.h>
+#include <mlk/core/sys.h>
+
+#include <assets/images/battle-background.h>
+#include <assets/images/black-cat.h>
+#include <assets/images/haunted-wood.h>
+
+#include <assets/sounds/fire.h>
+
+#include <assets/sprites/explosion.h>
+#include <assets/sprites/john-sword.h>
+#include <assets/sprites/john-walk.h>
+#include <assets/sprites/ui-cursor.h>
+
+#include "registry.h"
+
+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, data) \
+	{ (i), (data), sizeof (data) }
+
+static const struct {
+	enum registry_image index;
+	const unsigned char *data;
+	size_t datasz;
+} images[] = {
+	REGISTRY_IMAGE(REGISTRY_IMAGE_BATTLE_BACKGROUND, assets_images_battle_background)
+};
+
+#define REGISTRY_TEXTURE(s, data, cw, ch) \
+	{ (s), (data), sizeof (data), (cw), (ch) }
+
+static const struct {
+	enum registry_texture index;
+	const unsigned char *data;
+	size_t datasz;
+	unsigned int cellw;
+	unsigned int cellh;
+} textures[] = {
+	REGISTRY_TEXTURE(REGISTRY_TEXTURE_CURSOR, assets_sprites_ui_cursor, 24, 24),
+	REGISTRY_TEXTURE(REGISTRY_TEXTURE_EXPLOSION, assets_sprites_explosion, 256, 256),
+	REGISTRY_TEXTURE(REGISTRY_TEXTURE_JOHN_SWORD, assets_sprites_john_sword, 256, 256),
+	REGISTRY_TEXTURE(REGISTRY_TEXTURE_JOHN_WALK, assets_sprites_john_walk, 256, 256),
+	REGISTRY_TEXTURE(REGISTRY_TEXTURE_HAUNTED_WOOD, assets_images_haunted_wood, 0, 0),
+	REGISTRY_TEXTURE(REGISTRY_TEXTURE_BLACK_CAT, assets_images_black_cat, 0, 0)
+};
+
+#define REGISTRY_SOUND(s, data) \
+	{ (s), (data), sizeof (data) }
+
+static const struct {
+	enum registry_sound index;
+	const unsigned char *data;
+	size_t datasz;
+} sounds[] = {
+	REGISTRY_SOUND(REGISTRY_SOUND_FIRE, assets_sounds_fire)
+};
+
+static void
+load_images(void)
+{
+	for (size_t i = 0; i < UTIL_SIZE(images); ++i) {
+		struct texture *texture = &registry_images[images[i].index];
+
+		if (mlk_image_openmem(texture, images[i].data, images[i].datasz) < 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 (mlk_image_openmem(texture, textures[i].data, textures[i].datasz) < 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_openmem(sound, sounds[i].data, sounds[i].datasz) < 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/libmlk-example/mlk/example/registry.h	Sun Feb 26 19:44:47 2023 +0100
@@ -0,0 +1,66 @@
+/*
+ * registry.h -- registry of resources
+ *
+ * Copyright (c) 2020-2023 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER 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 <mlk/core/sound.h>
+#include <mlk/core/sprite.h>
+#include <mlk/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/libmlk-example/mlk/example/spell-fire.c	Sun Feb 26 19:44:47 2023 +0100
@@ -0,0 +1,132 @@
+/*
+ * spell-fire.c -- example of spell: fire
+ *
+ * Copyright (c) 2020-2023 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER 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 <mlk/core/action.h>
+#include <mlk/core/animation.h>
+#include <mlk/core/drawable.h>
+#include <mlk/core/alloc.h>
+
+#include <mlk/ui/align.h>
+
+#include <mlk/rpg/battle-state-check.h>
+#include <mlk/rpg/battle-state-rendering.h>
+#include <mlk/rpg/battle.h>
+#include <mlk/rpg/character.h>
+#include <mlk/rpg/spell.h>
+
+#include "registry.h"
+#include "spell-fire.h"
+
+struct self {
+	struct battle *battle;
+	struct character *target;
+	struct mlk_animation animation;
+	struct mlk_drawable drawable;
+	unsigned int selection;
+};
+
+static int
+update(struct mlk_drawable *dw, unsigned int ticks)
+{
+	struct self *self = dw->data;
+
+	return mlk_animation_update(&self->animation, ticks);
+}
+
+static void
+draw(struct mlk_drawable *dw)
+{
+	const struct self *self = dw->data;
+	const struct battle_entity *et = self->battle->enemies[self->selection];
+	const struct sprite *sprite = et->ch->sprites[CHARACTER_SPRITE_NORMAL];
+	int x, y;
+
+	align(ALIGN_CENTER,
+	    &x, &y, self->animation.sprite->cellw, self->animation.sprite->cellh,
+	    et->x, et->y, sprite->cellw, sprite->cellh);
+
+	mlk_animation_draw(&self->animation, x, y);
+}
+
+static void
+end(struct mlk_drawable *dw)
+{
+	struct self *self = dw->data;
+	struct character *ch = self->battle->enemies[self->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(self->battle, self->battle->enemies[self->selection]->ch, 100);
+	battle_state_check(self->battle);
+}
+
+static void
+finish(struct mlk_drawable *dw)
+{
+	free(dw->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 self *self;
+
+	(void)owner;
+
+	self = mlk_alloc_new0(1, sizeof (*self));
+	self->selection = slt->index_character;
+	self->battle = bt;
+	self->drawable.data = self;
+	self->drawable.update = update;
+	self->drawable.draw = draw;
+	self->drawable.finish = finish;
+	self->drawable.end = end;
+
+	mlk_animation_init(&self->animation, &registry_sprites[REGISTRY_TEXTURE_EXPLOSION], 12);
+	mlk_animation_start(&self->animation);
+
+	sound_play(&registry_sounds[REGISTRY_SOUND_FIRE]);
+	battle_state_rendering(bt, &self->drawable);
+}
+
+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/libmlk-example/mlk/example/spell-fire.h	Sun Feb 26 19:44:47 2023 +0100
@@ -0,0 +1,24 @@
+/*
+ * spell-fire.c -- example of spell: fire
+ *
+ * Copyright (c) 2020-2023 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER 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/libmlk-example/mlk/example/trace-hud.c	Sun Feb 26 19:44:47 2023 +0100
@@ -0,0 +1,145 @@
+/*
+ * trace_hud.c -- on screen hud
+ *
+ * Copyright (c) 2020-2023 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <mlk/core/action.h>
+#include <mlk/core/font.h>
+#include <mlk/core/trace.h>
+#include <mlk/core/window.h>
+
+#include <mlk/ui/label.h>
+#include <mlk/ui/theme.h>
+
+#include "trace-hud.h"
+
+#define LINES_MAX       (4)
+#define THEME(t)        ((t) ? (t) : theme_default())
+
+static struct {
+	char lines[LINES_MAX + 1][TRACE_LINE_MAX];
+	unsigned int elapsed;
+} data;
+
+struct trace_hud trace_hud = {
+	.timeout = TRACE_HUD_TIMEOUT_DEFAULT
+};
+
+void
+trace_hud_handler(const char *str)
+{
+	assert(str);
+
+	/* 1.Try to find an empty line. */
+	for (size_t i = 0; i < LINES_MAX; ++i) {
+		if (data.lines[i][0] == '\0') {
+			snprintf(data.lines[i], sizeof (data.lines[i]), "%s", str);
+			return;
+		}
+	}
+
+	/* 2. All lines are full, put in last one and move other. */
+	memmove(&data.lines[0], &data.lines[1], sizeof (data.lines[0]) * LINES_MAX - 1);
+	snprintf(data.lines[LINES_MAX - 1], sizeof (data.lines[0]), "%s", str);
+
+	/* 3. Reset elapsed time now. */
+	data.elapsed = 0;
+}
+
+void
+trace_hud_update(unsigned int ticks)
+{
+	data.elapsed += ticks;
+
+	/*
+	 * We have an empty line in the data.lines at LINES_MAX, so we simply so
+	 * to move the whole array.
+	 *
+	 * [0] = "abc"
+	 * [1] = "def"
+	 * [2] = "xyz"
+	 * [3] = "zef"
+	 * [n] = "ldkf"
+	 * [LINES_MAX + 1] = "\0"
+	 */
+	if (data.elapsed >= trace_hud.timeout) {
+		data.elapsed = 0;
+		memmove(&data.lines[0], &data.lines[1], sizeof (data.lines[0]) * LINES_MAX);
+	}
+}
+
+void
+trace_hud_draw(void)
+{
+	struct theme *th;
+	int x, y;
+
+	th = THEME(trace_hud.theme);
+	x = th->padding;
+	y = th->padding;
+
+	for (int i = 0; i < LINES_MAX && data.lines[i][0]; ++i) {
+		label_draw(&(struct label) {
+			.x = x,
+			.y = y,
+			.text = data.lines[i],
+			.theme = th,
+			.flags = LABEL_FLAGS_SHADOW
+		});
+
+		y += mlk_font_height(th->fonts[THEME_FONT_INTERFACE]);
+		y += th->padding;
+	}
+}
+
+void
+trace_hud_clear(void)
+{
+	memset(&data, 0, sizeof (data));
+}
+
+static int
+update(struct mlk_action *a, unsigned int ticks)
+{
+	(void)a;
+
+	trace_hud_update(ticks);
+
+	return 0;
+}
+
+static void
+draw(struct mlk_action *a)
+{
+	(void)a;
+
+	trace_hud_draw();
+}
+
+struct mlk_action *
+trace_hud_action(void)
+{
+	static struct mlk_action a = {
+		.update = update,
+		.draw = draw
+	};
+
+	return &a;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-example/mlk/example/trace-hud.h	Sun Feb 26 19:44:47 2023 +0100
@@ -0,0 +1,49 @@
+/*
+ * trace_hud.h -- on screen hud
+ *
+ * Copyright (c) 2020-2023 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER 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_TRACE_TRACE_HUD_H
+#define EXAMPLES_TRACE_TRACE_HUD_H
+
+#define TRACE_HUD_TIMEOUT_DEFAULT (3000)
+
+struct action;
+struct theme;
+
+struct trace_hud {
+	struct theme *theme;
+	unsigned int timeout;
+};
+
+extern struct trace_hud trace_hud;
+
+void
+trace_hud_handler(const char *);
+
+void
+trace_hud_update(unsigned int);
+
+void
+trace_hud_draw(void);
+
+void
+trace_hud_clear(void);
+
+struct mlk_action *
+trace_hud_action(void);
+
+#endif /* !EXAMPLES_TRACE_TRACE_HUD_H */