changeset 466:39dd7c66ebfd

core: script -> mlk_action_script While here, don't create a static array and let the user specify the storage area where actions will be put (similar to mlk_action_stack). Remove example-action for now which requires a major rewrite.
author David Demelier <markand@malikania.fr>
date Mon, 27 Feb 2023 11:03:13 +0100
parents 01f5580e43d1
children 7420c78018dc
files examples/CMakeLists.txt examples/example-action/CMakeLists.txt examples/example-action/example-action.c libmlk-core/CMakeLists.txt libmlk-core/mlk/core/action-script.c libmlk-core/mlk/core/action-script.h libmlk-core/mlk/core/script.c libmlk-core/mlk/core/script.h tests/test-action-script.c
diffstat 9 files changed, 266 insertions(+), 863 deletions(-) [+]
line wrap: on
line diff
--- a/examples/CMakeLists.txt	Mon Feb 27 10:03:52 2023 +0100
+++ b/examples/CMakeLists.txt	Mon Feb 27 11:03:13 2023 +0100
@@ -20,7 +20,6 @@
 
 set(
 	DIRS
-	example-action
 	example-animation
 	example-audio
 	example-battle
--- a/examples/example-action/CMakeLists.txt	Mon Feb 27 10:03:52 2023 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,33 +0,0 @@
-#
-# 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(example-action)
-
-set(
-	SOURCES
-	${example-action_SOURCE_DIR}/example-action.c
-)
-
-mlk_executable(
-	NAME example-action
-	FOLDER examples
-	LIBRARIES libmlk-example
-	SOURCES ${SOURCES}
-)
-
-source_group(TREE ${example-action_SOURCE_DIR} FILES ${SOURCES})
--- a/examples/example-action/example-action.c	Mon Feb 27 10:03:52 2023 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,424 +0,0 @@
-/*
- * example-action.c -- example on how to use automatic actions
- *
- * 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 <mlk/core/action.h>
-#include <mlk/core/action-stack.h>
-#include <mlk/core/core.h>
-#include <mlk/core/event.h>
-#include <mlk/core/game.h>
-#include <mlk/core/image.h>
-#include <mlk/core/maths.h>
-#include <mlk/core/painter.h>
-#include <mlk/core/panic.h>
-#include <mlk/core/script.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/theme.h>
-#include <mlk/ui/ui.h>
-
-#include <mlk/rpg/message.h>
-#include <mlk/rpg/rpg.h>
-
-#include <assets/sprites/chest.h>
-#include <assets/sprites/people.h>
-
-#define W       1280
-#define H       720
-
-#define MW      (W * 0.75)
-#define MX      ((W / 2) - (MW / 2))
-#define MY      (100)
-
-/* This is a stack of "parallel" events. */
-static struct mlk_action *events_actions[32];
-static struct mlk_action_stack events;
-
-/* This is a stack of modal events. */
-static struct mlk_action *modal_actions[32];
-static struct mlk_action_stack modal;
-
-/* Maximum number of states. */
-static struct state *states[1];
-
-/*
- * Event object for the chest to click on.
- */
-static struct {
-	const char *text[2];
-	struct message msg;
-	struct mlk_action msg_act;
-	int x;
-	int y;
-	int opened;
-	struct texture image;
-	struct sprite sprite;
-	struct mlk_action event;
-} chest = {
-	.text = {
-		"100000 pièces.",
-		"Te voilà riche sale file de crevette."
-	},
-	.msg = {
-		.x = MX,
-		.y = MY,
-		.w = MW,
-		.lines = chest.text,
-		.linesz = 2
-	}
-};
-
-/*
- * Event object for the guide who ask dialog with you.
- *
- * It is a script spawned upon click like this.
- *
- * [guide.event]
- *       |           spawn
- *   (on click)------------------->[message 1]
- *                                      v
- *                                 [message 2]
- *                                      v
- *                                 [question 3]
- *                   spawn              v
- * [response]<---------------------[check result]
- *     v
- * [message 4/5]
- */
-static struct {
-	struct {
-		const char *text[6];
-		struct message msg;
-		struct mlk_action act;
-	} msgs[5];
-
-	/* This is the sprite and action to click on. */
-	int x;
-	int y;
-	struct texture image;
-	struct sprite sprite;
-	struct mlk_action event;
-
-	/* This is the event for the response. */
-	struct mlk_action response;
-
-	struct script script;
-	struct mlk_action script_act;
-} guide = {
-	.msgs = {
-		{
-			.text = {
-				"Bienvenue dans ce monde Molko."
-			},
-			.msg = {
-				.x = MX,
-				.y = MY,
-				.w = MW,
-				.delay = MESSAGE_DELAY_DEFAULT,
-				.flags = MESSAGE_FLAGS_FADEIN,
-				.lines = guide.msgs[0].text,
-				.linesz = 1
-			},
-		},
-		{
-			.text = {
-				"Penses tu vraiment pouvoir me battre ?"
-			},
-			.msg = {
-				.x = MX,
-				.y = MY,
-				.w = MW,
-				.lines = guide.msgs[1].text,
-				.linesz = 1
-			}
-		},
-		{
-			.text = {
-				"Non j'ai la trouille.",
-				"Bien sûr, je suis la légende."
-			},
-			.msg = {
-				.x = MX,
-				.y = MY,
-				.w = MW,
-				.flags = MESSAGE_FLAGS_QUESTION,
-				.lines = guide.msgs[2].text,
-				.linesz = 2
-			}
-		},
-
-		/* In case of NO. */
-		{
-			.text = {
-				"Poule mouillée."
-			},
-			.msg = {
-				.x = MX,
-				.y = MY,
-				.w = MW,
-				.lines = guide.msgs[3].text,
-				.linesz = 1
-			}
-		},
-
-		/* In case of YES. */
-		{
-			.text = {
-				"Prépare toi à souffrir."
-			},
-			.msg = {
-				.x = MX,
-				.y = MY,
-				.w = MW,
-				.lines = guide.msgs[4].text,
-				.linesz = 1
-			}
-		}
-	}
-};
-
-static int
-guide_response_update(struct mlk_action *act, unsigned int ticks)
-{
-	/* Immediately return to get access to end. */
-	(void)act;
-	(void)ticks;
-
-	return 1;
-}
-
-static void
-guide_response_end(struct mlk_action *act)
-{
-	(void)act;
-
-	/* We add a final response depending on the result. */
-	const int index = guide.msgs[2].msg.index + 3;
-
-	message_action(&guide.msgs[index].msg, &guide.msgs[index].act);
-	message_query(&guide.msgs[index].msg, NULL, &guide.msgs[index].msg.h);
-	mlk_action_stack_add(&modal, &guide.msgs[index].act);
-}
-
-static void
-guide_popup(void)
-{
-	/* Prepare the script with first 3 messages. */
-	for (size_t i = 0; i < 3; ++i) {
-		message_action(&guide.msgs[i].msg, &guide.msgs[i].act);
-		message_query(&guide.msgs[i].msg, NULL, &guide.msgs[i].msg.h);
-		script_append(&guide.script, &guide.msgs[i].act);
-	}
-
-	/* Reset the message index. */
-	guide.msgs[2].msg.index = 0;
-
-	/* This is final action that analyze user input. */
-	guide.response.update = guide_response_update;
-	guide.response.end = guide_response_end;
-	script_append(&guide.script, &guide.response);
-
-	script_action(&guide.script, &guide.script_act);
-	mlk_action_stack_add(&modal, &guide.script_act);
-}
-
-static void
-guide_handle(struct mlk_action *act, const union mlk_event *ev)
-{
-	(void)act;
-
-	if (!mlk_action_stack_completed(&modal))
-		return;
-
-	switch (ev->type) {
-	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();
-		break;
-	default:
-		break;
-	}
-}
-
-static void
-guide_draw(struct mlk_action *act)
-{
-	(void)act;
-
-	sprite_draw(&guide.sprite, 0, 0, guide.x, guide.y);
-}
-
-static void
-guide_init(void)
-{
-	if (mlk_image_openmem(&guide.image, assets_sprites_people, sizeof (assets_sprites_people)) < 0)
-		mlk_panic();
-
-	sprite_init(&guide.sprite, &guide.image, 48, 48);
-
-	/* This is the global guide action. */
-	guide.event.handle = guide_handle;
-	guide.event.draw = guide_draw;
-	guide.x = 200;
-	guide.y = 200;
-}
-
-static void
-chest_handle(struct mlk_action *act, const union mlk_event *ev)
-{
-	(void)act;
-
-	if (chest.opened || !mlk_action_stack_completed(&modal))
-		return;
-
-	switch (ev->type) {
-	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);
-			mlk_action_stack_add(&modal, &chest.msg_act);
-		}
-	default:
-		break;
-	}
-}
-
-static void
-chest_draw(struct mlk_action *act)
-{
-	(void)act;
-
-	const int row = chest.opened ? chest.sprite.nrows - 1 : 0;
-	const int col = chest.opened ? chest.sprite.ncols - 1 : 0;
-
-	sprite_draw(&chest.sprite, row, col, chest.x, chest.y);
-}
-
-static void
-chest_init(void)
-{
-	if (mlk_image_openmem(&chest.image, assets_sprites_chest, sizeof (assets_sprites_chest)) < 0)
-		mlk_panic();
-
-	sprite_init(&chest.sprite, &chest.image, 32, 32);
-
-	chest.x = 100;
-	chest.y = 100;
-	chest.event.handle = chest_handle;
-	chest.event.draw = chest_draw;
-}
-
-static void
-init(void)
-{
-	if (mlk_core_init("fr.malikania", "example-action") < 0 || ui_init() < 0 || rpg_init() < 0)
-		mlk_panic();
-	if (window_open("Example - Action", W, H) < 0)
-		mlk_panic();
-
-	guide_init();
-	chest_init();
-}
-
-static void
-handle(struct state *st, const union mlk_event *ev)
-{
-	(void)st;
-
-	switch (ev->type) {
-	case MLK_EVENT_QUIT:
-		mlk_game_quit();
-		break;
-	default:
-		mlk_action_stack_handle(&events, ev);
-		mlk_action_stack_handle(&modal, ev);
-		break;
-	}
-}
-
-static void
-update(struct state *st, unsigned int ticks)
-{
-	(void)st;
-
-	mlk_action_stack_update(&events, ticks);
-	mlk_action_stack_update(&modal, ticks);
-}
-
-static void
-draw(struct state *st)
-{
-	(void)st;
-
-	mlk_painter_set_color(0xffffffff);
-	mlk_painter_clear();
-	mlk_action_stack_draw(&events);
-	mlk_action_stack_draw(&modal);
-	mlk_painter_present();
-}
-
-static void
-run(void)
-{
-	struct state state = {
-		.handle = handle,
-		.update = update,
-		.draw = draw
-	};
-
-	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);
-
-	mlk_game_init(states, UTIL_SIZE(states));
-	mlk_game_push(&state);
-	mlk_game_loop();
-}
-
-static void
-quit(void)
-{
-	window_finish();
-	rpg_finish();
-	ui_finish();
-	mlk_core_finish();
-}
-
-int
-main(int argc, char **argv)
-{
-	(void)argc;
-	(void)argv;
-
-	init();
-	run();
-	quit();
-
-	return 0;
-}
--- a/libmlk-core/CMakeLists.txt	Mon Feb 27 10:03:52 2023 +0100
+++ b/libmlk-core/CMakeLists.txt	Mon Feb 27 11:03:13 2023 +0100
@@ -20,6 +20,8 @@
 
 set(
 	SOURCES
+	${libmlk-core_SOURCE_DIR}/mlk/core/action-script.c
+	${libmlk-core_SOURCE_DIR}/mlk/core/action-script.h
 	${libmlk-core_SOURCE_DIR}/mlk/core/action-stack.c
 	${libmlk-core_SOURCE_DIR}/mlk/core/action-stack.h
 	${libmlk-core_SOURCE_DIR}/mlk/core/action.c
@@ -60,8 +62,6 @@
 	${libmlk-core_SOURCE_DIR}/mlk/core/painter.h
 	${libmlk-core_SOURCE_DIR}/mlk/core/panic.c
 	${libmlk-core_SOURCE_DIR}/mlk/core/panic.h
-	${libmlk-core_SOURCE_DIR}/mlk/core/script.c
-	${libmlk-core_SOURCE_DIR}/mlk/core/script.h
 	${libmlk-core_SOURCE_DIR}/mlk/core/sound.c
 	${libmlk-core_SOURCE_DIR}/mlk/core/sound.h
 	${libmlk-core_SOURCE_DIR}/mlk/core/sprite.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-core/mlk/core/action-script.c	Mon Feb 27 11:03:13 2023 +0100
@@ -0,0 +1,117 @@
+/*
+ * script.c -- convenient sequence of actions
+ *
+ * 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 <stdlib.h>
+#include <string.h>
+
+#include "action-script.h"
+#include "action.h"
+#include "err.h"
+
+static inline struct mlk_action *
+current(struct mlk_action_script *s)
+{
+	if (s->cur >= s->size)
+		return NULL;
+
+	return s->actions[s->cur];
+}
+
+void
+mlk_action_script_init(struct mlk_action_script *s, struct mlk_action **actions, size_t max)
+{
+	assert(s);
+	
+	s->actions = actions;
+	s->max = max;
+	s->size = s->cur = 0;
+}
+
+int
+mlk_action_script_append(struct mlk_action_script *s, struct mlk_action *a)
+{
+	assert(s);
+	assert(a);
+
+	if (s->size >= s->max)
+		return MLK_ERR_NO_MEM;
+
+	s->actions[s->size++] = a;
+
+	return 0;
+}
+
+void
+mlk_action_script_handle(struct mlk_action_script *s, const union mlk_event *ev)
+{
+	assert(s);
+	assert(ev);
+
+	struct mlk_action *a = current(s);
+
+	if (a)
+		mlk_action_handle(a, ev);
+}
+
+int
+mlk_action_script_update(struct mlk_action_script *s, unsigned int ticks)
+{
+	assert(s);
+
+	struct mlk_action *a;
+
+	/* No action left means completed. */
+	if (!(a = current(s)))
+		return 1;
+
+	if (mlk_action_update(a, ticks)) {
+		mlk_action_end(a);
+		s->cur++;
+	}
+
+	return s->cur >= s->size;
+}
+
+void
+mlk_action_script_draw(struct mlk_action_script *s)
+{
+	assert(s);
+
+	struct mlk_action *a;
+
+	if ((a = current(s)))
+		mlk_action_draw(a);
+}
+
+int
+mlk_action_script_completed(const struct mlk_action_script *s)
+{
+	assert(s);
+
+	return s->cur >= s->size;
+}
+
+void
+mlk_action_script_finish(struct mlk_action_script *s)
+{
+	assert(s);
+
+	for (size_t i = 0; i < s->size; ++i)
+		mlk_action_finish(s->actions[i]);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-core/mlk/core/action-script.h	Mon Feb 27 11:03:13 2023 +0100
@@ -0,0 +1,62 @@
+/*
+ * script.h -- convenient sequence of actions
+ *
+ * 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 MLK_CORE_ACTION_SCRIPT_H
+#define MLK_CORE_ACTION_SCRIPT_H
+
+#include <stddef.h>
+
+#include "core.h"
+
+struct mlk_action;
+
+union mlk_event;
+
+struct mlk_action_script {
+	struct mlk_action **actions;
+	size_t size;
+	size_t max;
+	size_t cur;
+};
+
+MLK_CORE_BEGIN_DECLS
+
+void
+mlk_action_script_init(struct mlk_action_script *, struct mlk_action **, size_t);
+
+int
+mlk_action_script_append(struct mlk_action_script *, struct mlk_action *);
+
+void
+mlk_action_script_handle(struct mlk_action_script *, const union mlk_event *);
+
+int
+mlk_action_script_update(struct mlk_action_script *, unsigned int);
+
+void
+mlk_action_script_draw(struct mlk_action_script *);
+
+int
+mlk_action_script_completed(const struct mlk_action_script *);
+
+void
+mlk_action_script_finish(struct mlk_action_script *);
+
+MLK_CORE_END_DECLS
+
+#endif /* !MLK_CORE_ACTION_SCRIPT_H */
--- a/libmlk-core/mlk/core/script.c	Mon Feb 27 10:03:52 2023 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,154 +0,0 @@
-/*
- * script.c -- convenient sequence of actions
- *
- * 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 <stdlib.h>
-#include <string.h>
-
-#include "action.h"
-#include "err.h"
-#include "script.h"
-
-static struct mlk_action *
-current(struct script *s)
-{
-	if (s->cur >= s->actionsz)
-		return NULL;
-
-	return s->actions[s->cur];
-}
-
-static void
-handle(struct mlk_action *a, const union mlk_event *ev)
-{
-	script_handle(a->data, ev);
-}
-
-static int
-update(struct mlk_action *a, unsigned int ticks)
-{
-	return script_update(a->data, ticks);
-}
-
-static void
-draw(struct mlk_action *a)
-{
-	script_draw(a->data);
-}
-
-static void
-finish(struct mlk_action *a)
-{
-	script_finish(a->data);
-}
-
-void
-script_init(struct script *s)
-{
-	assert(s);
-
-	memset(s, 0, sizeof (*s));
-}
-
-int
-script_append(struct script *s, struct mlk_action *a)
-{
-	assert(s);
-	assert(a);
-
-	if (s->actionsz >= SCRIPT_ACTION_MAX)
-		return MLK_ERR_NO_MEM;
-
-	s->actions[s->actionsz++] = a;
-
-	return 0;
-}
-
-void
-script_handle(struct script *s, const union mlk_event *ev)
-{
-	assert(s);
-	assert(ev);
-
-	struct mlk_action *a = current(s);
-
-	if (a)
-		mlk_action_handle(a, ev);
-}
-
-int
-script_update(struct script *s, unsigned int ticks)
-{
-	assert(s);
-
-	struct mlk_action *a = current(s);
-
-	if (!a)
-		return 1;
-
-	if (mlk_action_update(a, ticks)) {
-		mlk_action_end(a);
-		s->cur++;
-	}
-
-	return s->cur >= s->actionsz;
-}
-
-void
-script_draw(struct script *s)
-{
-	assert(s);
-
-	struct mlk_action *a = current(s);
-
-	if (a)
-		mlk_action_draw(a);
-}
-
-int
-script_completed(const struct script *s)
-{
-	assert(s);
-
-	return s->cur >= s->actionsz;
-}
-
-void
-script_finish(struct script *s)
-{
-	assert(s);
-
-	for (size_t i = 0; i < s->actionsz; ++i)
-		mlk_action_finish(s->actions[i]);
-
-	memset(s, 0, sizeof (*s));
-}
-
-void
-script_action(struct script *s, struct mlk_action *action)
-{
-	assert(s);
-	assert(action);
-
-	memset(action, 0, sizeof (*action));
-	action->data = s;
-	action->handle = handle;
-	action->update = update;
-	action->draw = draw;
-	action->finish = finish;
-}
--- a/libmlk-core/mlk/core/script.h	Mon Feb 27 10:03:52 2023 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,66 +0,0 @@
-/*
- * script.h -- convenient sequence of actions
- *
- * 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 MLK_CORE_SCRIPT_H
-#define MLK_CORE_SCRIPT_H
-
-#include <stddef.h>
-
-#include "core.h"
-
-#define SCRIPT_ACTION_MAX (128)
-
-struct mlk_action;
-
-union mlk_event;
-
-struct script {
-	struct mlk_action *actions[SCRIPT_ACTION_MAX];
-	size_t actionsz;
-	size_t cur;
-};
-
-MLK_CORE_BEGIN_DECLS
-
-void
-script_init(struct script *);
-
-int
-script_append(struct script *, struct mlk_action *);
-
-void
-script_handle(struct script *, const union mlk_event *);
-
-int
-script_update(struct script *, unsigned int);
-
-void
-script_draw(struct script *);
-
-int
-script_completed(const struct script *);
-
-void
-script_finish(struct script *);
-
-void
-script_action(struct script *s, struct mlk_action *);
-
-MLK_CORE_END_DECLS
-
-#endif /* !MLK_CORE_SCRIPT_H */
--- a/tests/test-action-script.c	Mon Feb 27 10:03:52 2023 +0100
+++ b/tests/test-action-script.c	Mon Feb 27 11:03:13 2023 +0100
@@ -17,8 +17,9 @@
  */
 
 #include <mlk/core/action.h>
+#include <mlk/core/err.h>
 #include <mlk/core/event.h>
-#include <mlk/core/script.h>
+#include <mlk/core/action-script.h>
 
 #include <dt.h>
 
@@ -88,39 +89,50 @@
 static void
 test_basics_init(void)
 {
-	struct script sc;
+	struct mlk_action_script sc;
+	struct mlk_action *actions[10];
 
-	script_init(&sc);
+	mlk_action_script_init(&sc, actions, 10);
 
-	DT_EQ_UINT(sc.actionsz, 0U);
-	DT_EQ_UINT(sc.cur, 0U);
+	DT_EQ_SIZE(sc.size, 0U);
+	DT_EQ_SIZE(sc.max, 10U);
+	DT_EQ_SIZE(sc.cur, 0U);
 }
 
 static void
 test_basics_append(void)
 {
-	struct mlk_action act[3] = {0};
-	struct script sc = {0};
+	struct mlk_action actions[4] = {0};
+	struct mlk_action *array[3] = {0};
+	struct mlk_action_script sc = {0};
+
+	mlk_action_script_init(&sc, array, 3);
 
-	DT_ASSERT(script_append(&sc, &act[0]) == 0);
-	DT_EQ_UINT(sc.cur, 0U);
-	DT_EQ_UINT(sc.actionsz, 1U);
-	DT_EQ_PTR(sc.actions[0], &act[0]);
+	DT_ASSERT(mlk_action_script_append(&sc, &actions[0]) == 0);
+	DT_EQ_SIZE(sc.size, 1U);
+	DT_EQ_SIZE(sc.max, 3U);
+	DT_EQ_SIZE(sc.cur, 0U);
+	DT_EQ_PTR(sc.actions[0], &actions[0]);
 
-	DT_ASSERT(script_append(&sc, &act[1]) == 0);
-	DT_EQ_UINT(sc.cur, 0U);
-	DT_EQ_UINT(sc.actionsz, 2U);
-	DT_EQ_PTR(sc.actions[0], &act[0]);
-	DT_EQ_PTR(sc.actions[1], &act[1]);
+	DT_ASSERT(mlk_action_script_append(&sc, &actions[1]) == 0);
+	DT_EQ_SIZE(sc.size, 2U);
+	DT_EQ_SIZE(sc.max, 3U);
+	DT_EQ_SIZE(sc.cur, 0U);
+	DT_EQ_PTR(sc.actions[0], &actions[0]);
+	DT_EQ_PTR(sc.actions[1], &actions[1]);
 
-	DT_ASSERT(script_append(&sc, &act[2]) == 0);
-	DT_EQ_UINT(sc.cur, 0U);
-	DT_EQ_UINT(sc.actionsz, 3U);
-	DT_EQ_PTR(sc.actions[0], &act[0]);
-	DT_EQ_PTR(sc.actions[1], &act[1]);
-	DT_EQ_PTR(sc.actions[2], &act[2]);
+	DT_ASSERT(mlk_action_script_append(&sc, &actions[2]) == 0);
+	DT_EQ_SIZE(sc.size, 3U);
+	DT_EQ_SIZE(sc.max, 3U);
+	DT_EQ_SIZE(sc.cur, 0U);
+	DT_EQ_PTR(sc.actions[0], &actions[0]);
+	DT_EQ_PTR(sc.actions[1], &actions[1]);
+	DT_EQ_PTR(sc.actions[2], &actions[2]);
 
-	script_finish(&sc);
+	/* This can not fit. */
+	DT_ASSERT(mlk_action_script_append(&sc, &actions[3]) == MLK_ERR_NO_MEM);
+
+	mlk_action_script_finish(&sc);
 }
 
 static void
@@ -135,14 +147,17 @@
 		{ .act = INIT(&table[2].inv, my_update_false)   }
 	};
 
-	struct script sc = {0};
+	struct mlk_action array[3] = {0};
+	struct mlk_action_script sc = {0};
 
-	DT_ASSERT(script_append(&sc, &table[0].act) == 0);
-	DT_ASSERT(script_append(&sc, &table[1].act) == 0);
-	DT_ASSERT(script_append(&sc, &table[2].act) == 0);
+	mlk_action_script_init(&sc, array, 3);
+
+	DT_ASSERT(mlk_action_script_append(&sc, &table[0].act) == 0);
+	DT_ASSERT(mlk_action_script_append(&sc, &table[1].act) == 0);
+	DT_ASSERT(mlk_action_script_append(&sc, &table[2].act) == 0);
 
 	/* [0] */
-	script_handle(&sc, &(union mlk_event){0});
+	mlk_action_script_handle(&sc, &(union mlk_event){0});
 	DT_EQ_INT(table[0].inv.handle, 1);
 	DT_EQ_INT(table[0].inv.update, 0);
 	DT_EQ_INT(table[0].inv.draw, 0);
@@ -160,8 +175,8 @@
 	DT_EQ_INT(table[2].inv.finish, 0);
 
 	/* [0] -> [1] */
-	DT_ASSERT(!script_update(&sc, 0));
-	script_handle(&sc, &(union mlk_event){0});
+	DT_ASSERT(!mlk_action_script_update(&sc, 0));
+	mlk_action_script_handle(&sc, &(union mlk_event){0});
 
 	DT_EQ_INT(table[0].inv.handle, 1);
 	DT_EQ_INT(table[0].inv.update, 1);
@@ -180,8 +195,8 @@
 	DT_EQ_INT(table[2].inv.finish, 0);
 
 	/* [2] */
-	DT_ASSERT(!script_update(&sc, 0));
-	script_handle(&sc, &(union mlk_event){0});
+	DT_ASSERT(!mlk_action_script_update(&sc, 0));
+	mlk_action_script_handle(&sc, &(union mlk_event){0});
 
 	DT_EQ_INT(table[0].inv.handle, 1);
 	DT_EQ_INT(table[0].inv.update, 1);
@@ -198,6 +213,8 @@
 	DT_EQ_INT(table[2].inv.draw, 0);
 	DT_EQ_INT(table[2].inv.end, 0);
 	DT_EQ_INT(table[2].inv.finish, 0);
+
+	mlk_action_script_finish(&sc);
 }
 
 static void
@@ -212,14 +229,17 @@
 		{ .act = INIT(&table[2].inv, my_update_false)   }
 	};
 
-	struct script sc = {0};
+	struct mlk_action array[3];
+	struct mlk_action_script sc = {0};
 
-	DT_ASSERT(script_append(&sc, &table[0].act) == 0);
-	DT_ASSERT(script_append(&sc, &table[1].act) == 0);
-	DT_ASSERT(script_append(&sc, &table[2].act) == 0);
+	mlk_action_script_init(&sc, array, 3);
+
+	DT_ASSERT(mlk_action_script_append(&sc, &table[0].act) == 0);
+	DT_ASSERT(mlk_action_script_append(&sc, &table[1].act) == 0);
+	DT_ASSERT(mlk_action_script_append(&sc, &table[2].act) == 0);
 
 	/* 0 -> 1 */
-	DT_ASSERT(!script_update(&sc, 0));
+	DT_ASSERT(!mlk_action_script_update(&sc, 0));
 	DT_EQ_INT(table[0].inv.handle, 0);
 	DT_EQ_INT(table[0].inv.update, 1);
 	DT_EQ_INT(table[0].inv.draw, 0);
@@ -237,7 +257,7 @@
 	DT_EQ_INT(table[2].inv.finish, 0);
 
 	/* 1 -> 2 */
-	DT_ASSERT(!script_update(&sc, 0));
+	DT_ASSERT(!mlk_action_script_update(&sc, 0));
 	DT_EQ_INT(table[0].inv.handle, 0);
 	DT_EQ_INT(table[0].inv.update, 1);
 	DT_EQ_INT(table[0].inv.draw, 0);
@@ -255,9 +275,9 @@
 	DT_EQ_INT(table[2].inv.finish, 0);
 
 	/* 2 stays, it never ends. */
-	DT_ASSERT(!script_update(&sc, 0));
-	DT_ASSERT(!script_update(&sc, 0));
-	DT_ASSERT(!script_update(&sc, 0));
+	DT_ASSERT(!mlk_action_script_update(&sc, 0));
+	DT_ASSERT(!mlk_action_script_update(&sc, 0));
+	DT_ASSERT(!mlk_action_script_update(&sc, 0));
 	DT_EQ_INT(table[0].inv.handle, 0);
 	DT_EQ_INT(table[0].inv.update, 1);
 	DT_EQ_INT(table[0].inv.draw, 0);
@@ -276,7 +296,7 @@
 
 	/* Now, change its update function to complete the script. */
 	table[2].act.update = my_update_true;
-	DT_ASSERT(script_update(&sc, 0));
+	DT_ASSERT(mlk_action_script_update(&sc, 0));
 	DT_EQ_INT(table[0].inv.handle, 0);
 	DT_EQ_INT(table[0].inv.update, 1);
 	DT_EQ_INT(table[0].inv.draw, 0);
@@ -292,6 +312,8 @@
 	DT_EQ_INT(table[2].inv.draw, 0);
 	DT_EQ_INT(table[2].inv.end, 1);
 	DT_EQ_INT(table[2].inv.finish, 0);
+
+	mlk_action_script_finish(&sc);
 }
 
 static void
@@ -306,14 +328,17 @@
 		{ .act = INIT(&table[2].inv, my_update_false)   }
 	};
 
-	struct script sc = {0};
+	struct mlk_action array[3];
+	struct mlk_action_script sc = {0};
 
-	DT_ASSERT(script_append(&sc, &table[0].act) == 0);
-	DT_ASSERT(script_append(&sc, &table[1].act) == 0);
-	DT_ASSERT(script_append(&sc, &table[2].act) == 0);
+	mlk_action_script_init(&sc, array, 3);
+
+	DT_ASSERT(mlk_action_script_append(&sc, &table[0].act) == 0);
+	DT_ASSERT(mlk_action_script_append(&sc, &table[1].act) == 0);
+	DT_ASSERT(mlk_action_script_append(&sc, &table[2].act) == 0);
 
 	/* [0] */
-	script_draw(&sc);
+	mlk_action_script_draw(&sc);
 	DT_EQ_INT(table[0].inv.handle, 0);
 	DT_EQ_INT(table[0].inv.update, 0);
 	DT_EQ_INT(table[0].inv.draw, 1);
@@ -331,8 +356,8 @@
 	DT_EQ_INT(table[2].inv.finish, 0);
 
 	/* [0] -> [1] */
-	DT_ASSERT(!script_update(&sc, 0));
-	script_draw(&sc);
+	DT_ASSERT(!mlk_action_script_update(&sc, 0));
+	mlk_action_script_draw(&sc);
 
 	DT_EQ_INT(table[0].inv.handle, 0);
 	DT_EQ_INT(table[0].inv.update, 1);
@@ -351,8 +376,8 @@
 	DT_EQ_INT(table[2].inv.finish, 0);
 
 	/* [2] */
-	DT_ASSERT(!script_update(&sc, 0));
-	script_draw(&sc);
+	DT_ASSERT(!mlk_action_script_update(&sc, 0));
+	mlk_action_script_draw(&sc);
 
 	DT_EQ_INT(table[0].inv.handle, 0);
 	DT_EQ_INT(table[0].inv.update, 1);
@@ -383,16 +408,19 @@
 		{ .act = INIT(&table[2].inv, my_update_false)   }
 	};
 
-	struct script sc = {0};
+	struct mlk_action array[3];
+	struct mlk_action_script sc = {0};
 
-	DT_ASSERT(script_append(&sc, &table[0].act) == 0);
-	DT_ASSERT(script_append(&sc, &table[1].act) == 0);
-	DT_ASSERT(script_append(&sc, &table[2].act) == 0);
+	mlk_action_script_init(&sc, array, 3);
+
+	DT_ASSERT(mlk_action_script_append(&sc, &table[0].act) == 0);
+	DT_ASSERT(mlk_action_script_append(&sc, &table[1].act) == 0);
+	DT_ASSERT(mlk_action_script_append(&sc, &table[2].act) == 0);
 
 	/* Update once so that the current action goes to 1. */
-	DT_ASSERT(!script_update(&sc, 0));
+	DT_ASSERT(!mlk_action_script_update(&sc, 0));
 
-	script_finish(&sc);
+	mlk_action_script_finish(&sc);
 
 	DT_EQ_INT(table[0].inv.handle, 0);
 	DT_EQ_INT(table[0].inv.update, 1);
@@ -411,131 +439,6 @@
 	DT_EQ_INT(table[2].inv.finish, 1);
 }
 
-static void
-test_action_simple(void)
-{
-	struct {
-		struct invokes inv;
-		struct mlk_action act;
-	} table[] = {
-		{ .act = INIT(&table[0].inv, my_update_true)    },
-		{ .act = INIT(&table[1].inv, my_update_true)    },
-		{ .act = INIT(&table[2].inv, my_update_false)   }
-	};
-
-	struct script sc = {0};
-	struct mlk_action act;
-
-	DT_ASSERT(script_append(&sc, &table[0].act) == 0);
-	DT_ASSERT(script_append(&sc, &table[1].act) == 0);
-	DT_ASSERT(script_append(&sc, &table[2].act) == 0);
-
-	/* Now convert this script into an action itself. */
-	script_action(&sc, &act);
-
-	/* Draw and input before updating. */
-	mlk_action_handle(&act, &(union mlk_event){0});
-	mlk_action_draw(&act);
-
-	/* [0] -> [1] */
-	DT_ASSERT(!mlk_action_update(&act, 0));
-	DT_EQ_INT(table[0].inv.handle, 1);
-	DT_EQ_INT(table[0].inv.update, 1);
-	DT_EQ_INT(table[0].inv.draw, 1);
-	DT_EQ_INT(table[0].inv.end, 1);
-	DT_EQ_INT(table[0].inv.finish, 0);
-	DT_EQ_INT(table[1].inv.handle, 0);
-	DT_EQ_INT(table[1].inv.update, 0);
-	DT_EQ_INT(table[1].inv.draw, 0);
-	DT_EQ_INT(table[1].inv.end, 0);
-	DT_EQ_INT(table[1].inv.finish, 0);
-	DT_EQ_INT(table[2].inv.handle, 0);
-	DT_EQ_INT(table[2].inv.update, 0);
-	DT_EQ_INT(table[2].inv.draw, 0);
-	DT_EQ_INT(table[2].inv.end, 0);
-	DT_EQ_INT(table[2].inv.finish, 0);
-
-	mlk_action_handle(&act, &(union mlk_event){0});
-	mlk_action_draw(&act);
-
-	/* [1] -> [2] */
-	DT_ASSERT(!mlk_action_update(&act, 0));
-	DT_EQ_INT(table[0].inv.handle, 1);
-	DT_EQ_INT(table[0].inv.update, 1);
-	DT_EQ_INT(table[0].inv.draw, 1);
-	DT_EQ_INT(table[0].inv.end, 1);
-	DT_EQ_INT(table[0].inv.finish, 0);
-	DT_EQ_INT(table[1].inv.handle, 1);
-	DT_EQ_INT(table[1].inv.update, 1);
-	DT_EQ_INT(table[1].inv.draw, 1);
-	DT_EQ_INT(table[1].inv.end, 1);
-	DT_EQ_INT(table[1].inv.finish, 0);
-	DT_EQ_INT(table[2].inv.handle, 0);
-	DT_EQ_INT(table[2].inv.update, 0);
-	DT_EQ_INT(table[2].inv.draw, 0);
-	DT_EQ_INT(table[2].inv.end, 0);
-	DT_EQ_INT(table[2].inv.finish, 0);
-
-	mlk_action_handle(&act, &(union mlk_event){0});
-	mlk_action_draw(&act);
-
-	/* 2 stays, it never ends. */
-	DT_ASSERT(!mlk_action_update(&act, 0));
-	DT_ASSERT(!mlk_action_update(&act, 0));
-	DT_ASSERT(!mlk_action_update(&act, 0));
-	DT_EQ_INT(table[0].inv.handle, 1);
-	DT_EQ_INT(table[0].inv.update, 1);
-	DT_EQ_INT(table[0].inv.draw, 1);
-	DT_EQ_INT(table[0].inv.end, 1);
-	DT_EQ_INT(table[0].inv.finish, 0);
-	DT_EQ_INT(table[1].inv.handle, 1);
-	DT_EQ_INT(table[1].inv.update, 1);
-	DT_EQ_INT(table[1].inv.draw, 1);
-	DT_EQ_INT(table[1].inv.end, 1);
-	DT_EQ_INT(table[1].inv.finish, 0);
-	DT_EQ_INT(table[2].inv.handle, 1);
-	DT_EQ_INT(table[2].inv.update, 3);
-	DT_EQ_INT(table[2].inv.draw, 1);
-	DT_EQ_INT(table[2].inv.end, 0);
-	DT_EQ_INT(table[2].inv.finish, 0);
-
-	table[2].act.update = my_update_true;
-	DT_ASSERT(mlk_action_update(&act, 0));
-	DT_EQ_INT(table[0].inv.handle, 1);
-	DT_EQ_INT(table[0].inv.update, 1);
-	DT_EQ_INT(table[0].inv.draw, 1);
-	DT_EQ_INT(table[0].inv.end, 1);
-	DT_EQ_INT(table[0].inv.finish, 0);
-	DT_EQ_INT(table[1].inv.handle, 1);
-	DT_EQ_INT(table[1].inv.update, 1);
-	DT_EQ_INT(table[1].inv.draw, 1);
-	DT_EQ_INT(table[1].inv.end, 1);
-	DT_EQ_INT(table[1].inv.finish, 0);
-	DT_EQ_INT(table[2].inv.handle, 1);
-	DT_EQ_INT(table[2].inv.update, 4);
-	DT_EQ_INT(table[2].inv.draw, 1);
-	DT_EQ_INT(table[2].inv.end, 1);
-	DT_EQ_INT(table[2].inv.finish, 0);
-
-	/* Also dispose resources. */
-	mlk_action_finish(&act);
-	DT_EQ_INT(table[0].inv.handle, 1);
-	DT_EQ_INT(table[0].inv.update, 1);
-	DT_EQ_INT(table[0].inv.draw, 1);
-	DT_EQ_INT(table[0].inv.end, 1);
-	DT_EQ_INT(table[0].inv.finish, 1);
-	DT_EQ_INT(table[1].inv.handle, 1);
-	DT_EQ_INT(table[1].inv.update, 1);
-	DT_EQ_INT(table[1].inv.draw, 1);
-	DT_EQ_INT(table[1].inv.end, 1);
-	DT_EQ_INT(table[1].inv.finish, 1);
-	DT_EQ_INT(table[2].inv.handle, 1);
-	DT_EQ_INT(table[2].inv.update, 4);
-	DT_EQ_INT(table[2].inv.draw, 1);
-	DT_EQ_INT(table[2].inv.end, 1);
-	DT_EQ_INT(table[2].inv.finish, 1);
-}
-
 int
 main(void)
 {
@@ -545,6 +448,5 @@
 	DT_RUN(test_basics_update);
 	DT_RUN(test_basics_draw);
 	DT_RUN(test_basics_finish);
-	DT_RUN(test_action_simple);
 	DT_SUMMARY();
 }