Mercurial > molko
changeset 479:8e41ed5474cf
examples: resurrect example-action
author | David Demelier <markand@malikania.fr> |
---|---|
date | Mon, 27 Feb 2023 16:33:33 +0100 |
parents | 8c1a965b3019 |
children | c1f64d451230 |
files | examples/CMakeLists.txt examples/example-action/CMakeLists.txt examples/example-action/example-action.c examples/example-animation/example-animation.c libmlk-example/CMakeLists.txt libmlk-example/mlk/example/example.c libmlk-example/mlk/example/example.h libmlk-example/mlk/example/registry.c libmlk-example/mlk/example/registry.h |
diffstat | 9 files changed, 658 insertions(+), 15 deletions(-) [+] |
line wrap: on
line diff
--- a/examples/CMakeLists.txt Mon Feb 27 12:49:39 2023 +0100 +++ b/examples/CMakeLists.txt Mon Feb 27 16:33:33 2023 +0100 @@ -20,6 +20,7 @@ set( DIRS + example-action example-animation example-audio example-battle
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/example-action/CMakeLists.txt Mon Feb 27 16:33:33 2023 +0100 @@ -0,0 +1,33 @@ +# +# 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})
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/example-action/example-action.c Mon Feb 27 16:33:33 2023 +0100 @@ -0,0 +1,509 @@ +/* + * 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 <string.h> + +#include <mlk/core/action-script.h> +#include <mlk/core/action-stack.h> +#include <mlk/core/action.h> +#include <mlk/core/err.h> +#include <mlk/core/event.h> +#include <mlk/core/game.h> +#include <mlk/core/maths.h> +#include <mlk/core/painter.h> +#include <mlk/core/panic.h> +#include <mlk/core/sprite.h> +#include <mlk/core/state.h> +#include <mlk/core/texture.h> +#include <mlk/core/trace.h> +#include <mlk/core/util.h> + +#include <mlk/example/example.h> +#include <mlk/example/registry.h> + +#include <mlk/ui/align.h> +#include <mlk/ui/label.h> + +#include <mlk/rpg/message.h> + +/* Message width is 80% of window width and height is auto computed. */ +#define QMW (MLK_EXAMPLE_W * 0.8) + +/* Position of each message. */ +#define QMX ((MLK_EXAMPLE_W - QMW) / 2) +#define QMY (MLK_EXAMPLE_H * 0.2) + +/* Fading time and spacing. */ +#define QMD 250 +#define QMS 10 + +static struct mlk_state *states[8]; + +/* + * Those are two chests shown on the script that the user can click. They will + * fill up the script scene. + * + * For convenience, we arrange those two chests into a mlk_action_stack to + * avoid calling all mlk_action functions for every chests. + */ +static void chest_handle(struct mlk_action *, const union mlk_event *); +static void chest_draw(struct mlk_action *); + +static struct chest { + int x; + int y; + int open; + struct mlk_sprite *sprite; + struct mlk_action action; +} chests[2] = { + [0] = { + .sprite = ®istry_sprites[REGISTRY_TEXTURE_CHEST], + .action = { + .data = &chests[0], + .draw = chest_draw, + .handle = chest_handle, + } + }, + [1] = { + .sprite = ®istry_sprites[REGISTRY_TEXTURE_CHEST], + .action = { + .data = &chests[1], + .draw = chest_draw, + .handle = chest_handle, + } + } +}; + +/* + * This structure is the item that will be pushed into the mlk_action_script + * to create a sequence of message the user has to complete. + */ +struct item { + struct message msg; + struct mlk_action act; +}; + +static struct mlk_action *chests_stack_actions[16] = { + [0] = &chests[0].action, + [1] = &chests[1].action +}; +static struct mlk_action_stack chests_stack = { + .actions = chests_stack_actions, + .actionsz = MLK_UTIL_SIZE(chests_stack_actions) +}; + +/* + * Just a label to explain to the user what to do. + */ +static struct label label = { + .flags = LABEL_FLAGS_SHADOW, + .text = "Select your destiny." +}; + +static void item_handle(struct mlk_action *, const union mlk_event *); +static int item_update(struct mlk_action *, unsigned int); +static void item_draw(struct mlk_action *); + +static void script_init_quest1(void); +static void script_init_quest2(void); + +static void terminate_quest1(struct mlk_action *); +static void terminate_quest2(struct mlk_action *); + +static struct item quest1[] = { + { + .msg = { + .flags = MESSAGE_FLAGS_FADEIN, + .linesz = 1, + .lines = (const char *[]) { + "Welcome to this game." + } + }, + .act = { + .data = &quest1[0].msg, + .handle = item_handle, + .update = item_update, + .draw = item_draw + } + }, + { + .msg = { + .linesz = 3, + .lines = (const char *[]) { + "Your journey will begin in this dangerous world,", + "you must take this before going any further", + "Are you ready?" + } + }, + .act = { + .data = &quest1[1].msg, + .handle = item_handle, + .update = item_update, + .draw = item_draw + } + }, + { + .msg = { + .flags = MESSAGE_FLAGS_QUESTION, + .linesz = 2, + .lines = (const char *[]) { + "Of course I am", + "No I'm not sure" + } + }, + .act = { + .data = &quest1[2].msg, + .handle = item_handle, + .update = item_update, + .draw = item_draw, + .end = terminate_quest1 + } + }, + + /* + * This final action is a placeholder that will be updated once the + * last message question is completed through the end callback. + */ + { + .msg = { + .flags = MESSAGE_FLAGS_FADEOUT + }, + .act = { + .data = &quest1[3].msg, + .handle = item_handle, + .update = item_update, + .draw = item_draw + } + } +}; + +static struct item quest2[] = { + { + .msg = { + .flags = MESSAGE_FLAGS_FADEIN, + .linesz = 1, + .lines = (const char *[]) { + "Why did you select this chest?" + } + }, + .act = { + .data = &quest2[0].msg, + .handle = item_handle, + .update = item_update, + .draw = item_draw + } + }, + { + .msg = { + .flags = MESSAGE_FLAGS_QUESTION, + .linesz = 2, + .lines = (const char *[]) { + "Because I think there was some gold", + "I have no freaking idea" + } + }, + .act = { + .data = &quest2[1].msg, + .handle = item_handle, + .update = item_update, + .draw = item_draw, + .end = terminate_quest2 + } + }, + + /* See comment in quest 1. */ + { + .msg = { + .flags = MESSAGE_FLAGS_FADEOUT + }, + .act = { + .data = &quest2[2].msg, + .handle = item_handle, + .update = item_update, + .draw = item_draw + } + } +}; + +/* + * This is the main script that the loop will execute, we will initialize it + * depending on which chest the player clicks. + */ +static struct mlk_action *script_actions[16]; +static struct mlk_action_script script; + +static void +chest_handle(struct mlk_action *act, const union mlk_event *ev) +{ + struct chest *chest = act->data; + unsigned int cw, ch; + + /* Make sure that we don't operate on a already opened chest. */ + if (chest->open) + return; + + /* + * We are only interested if the event is a click on the chest itself + * so we have to test because actions have no notion of geometry. + */ + if (ev->type != MLK_EVENT_CLICKDOWN) + return; + + cw = registry_sprites[REGISTRY_TEXTURE_CHEST].cellw; + ch = registry_sprites[REGISTRY_TEXTURE_CHEST].cellh; + + if (!mlk_maths_is_boxed(chest->x, chest->y, cw, ch, ev->click.x, ev->click.y)) + return; + + /* Also, make sure that we don't operate on a already opened chest. */ + chest->open = 1; + + /* + * Now depending on the chest, update the main script with actual code + * we're interested in. + */ + memset(script_actions, 0, sizeof (script_actions)); + mlk_action_script_init(&script, script_actions, MLK_UTIL_SIZE(script_actions)); + + if (chest == &chests[0]) + script_init_quest1(); + else + script_init_quest2(); +} + +static void +chest_draw(struct mlk_action *act) +{ + const struct chest *chest = act->data; + int column; + + if (chest->open) + column = 3; + else + column = 0; + + mlk_sprite_draw(chest->sprite, 0, column, chest->x, chest->y); +} + +static void +item_handle(struct mlk_action *act, const union mlk_event *ev) +{ + message_handle(act->data, ev); +} + +static int +item_update(struct mlk_action *act, unsigned int ticks) +{ + return message_update(act->data, ticks); +} + +static void +item_draw(struct mlk_action *act) +{ + message_draw(act->data); +} + +static void +script_init_quest(struct item *m, size_t n) +{ + int err; + + for (size_t i = 0; i < n; ++i) { + m[i].msg.x = QMX; + m[i].msg.y = QMY; + m[i].msg.w = QMW; + m[i].msg.delay = QMD; + m[i].msg.spacing = QMS; + + message_start(&m[i].msg); + message_query(&m[i].msg, NULL, &m[i].msg.h); + + if ((err = mlk_action_script_append(&script, &m[i].act)) < 0) + mlk_panicf("%s", mlk_err_string(err)); + } +} + +static void +script_init_quest1(void) +{ + script_init_quest(quest1, MLK_UTIL_SIZE(quest1)); +} + +static void +terminate_quest1(struct mlk_action *act) +{ + static const char *line; + + struct message *cur = act->data; + struct message *nxt = &quest1[3].msg; + + nxt->linesz = 1; + nxt->lines = &line; + + if (cur->index == 0) + line = "Don't be so confident"; + else + line = "Nevermind, I'll do it myself"; + + message_query(nxt, NULL, &nxt->h); +} + +static void +terminate_quest2(struct mlk_action *act) +{ + static const char *line; + + struct message *cur = act->data; + struct message *nxt = &quest2[2].msg; + + nxt->linesz = 1; + nxt->lines = &line; + + if (cur->index == 0) + line = "Go away!"; + else + line = "Install OpenBSD then"; + + message_query(nxt, NULL, &nxt->h); +} + +static void +script_init_quest2(void) +{ + script_init_quest(quest2, MLK_UTIL_SIZE(quest2)); +} + +static void +chests_init(void) +{ + /* + * Chest #0 is at center 1st half of the screen. + * Chest #1 is at center of 2nd half of the sreen. + */ + const unsigned int cw = registry_sprites[REGISTRY_TEXTURE_CHEST].cellw; + const unsigned int ch = registry_sprites[REGISTRY_TEXTURE_CHEST].cellh; + + align(ALIGN_CENTER, &chests[0].x, &chests[0].y, cw, ch, + 0, 0, MLK_EXAMPLE_W / 2, MLK_EXAMPLE_H); + align(ALIGN_CENTER, &chests[1].x, &chests[1].y, cw, ch, + MLK_EXAMPLE_W / 2, 0, MLK_EXAMPLE_W / 2, MLK_EXAMPLE_H); +} + +static void +label_init(void) +{ + unsigned int lw, lh; + + label_query(&label, &lw, &lh); + align(ALIGN_CENTER, &label.x, &label.y, lw, lh, + 0, 0, MLK_EXAMPLE_W, MLK_EXAMPLE_H / 2); +} + +static void +init(void) +{ + int err; + + if ((err = mlk_example_init("example-action")) < 0) + mlk_panicf("example_init: %s", mlk_err_string(err)); + + chests_init(); + label_init(); +} + +static void +handle(struct mlk_state *st, const union mlk_event *ev) +{ + (void)st; + + switch (ev->type) { + case MLK_EVENT_QUIT: + mlk_game_quit(); + break; + default: + /* + * The main script is "modal", we must not update anything else than + * it until it's complete. + */ + if (mlk_action_script_completed(&script)) + mlk_action_stack_handle(&chests_stack, ev); + else + mlk_action_script_handle(&script, ev); + break; + } +} + +static void +update(struct mlk_state *st, unsigned int ticks) +{ + (void)st; + + mlk_action_stack_update(&chests_stack, ticks); + + if (mlk_action_script_update(&script, ticks)) + mlk_action_script_finish(&script); +} + +static void +draw(struct mlk_state *st) +{ + (void)st; + + mlk_painter_set_color(0x4f8fbaff); + mlk_painter_clear(); + label_draw(&label); + mlk_action_stack_draw(&chests_stack); + + /* + * Always draw the script in the end in case it overlaps everything + * else. + */ + mlk_action_script_draw(&script); + mlk_painter_present(); +} + +static void +run(void) +{ + struct mlk_state state = { + .handle = handle, + .update = update, + .draw = draw + }; + + mlk_game_init(states, MLK_UTIL_SIZE(states)); + mlk_game_push(&state); + mlk_game_loop(); +} + +static void +quit(void) +{ + mlk_example_finish(); +} + +int +main(int argc, char **argv) +{ + (void)argc; + (void)argv; + + init(); + run(); + quit(); +}
--- a/examples/example-animation/example-animation.c Mon Feb 27 12:49:39 2023 +0100 +++ b/examples/example-animation/example-animation.c Mon Feb 27 16:33:33 2023 +0100 @@ -145,4 +145,3 @@ run(); quit(); } -
--- a/libmlk-example/CMakeLists.txt Mon Feb 27 12:49:39 2023 +0100 +++ b/libmlk-example/CMakeLists.txt Mon Feb 27 16:33:33 2023 +0100 @@ -22,6 +22,8 @@ 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/example.c + ${libmlk-example_SOURCE_DIR}/mlk/example/example.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
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libmlk-example/mlk/example/example.c Mon Feb 27 16:33:33 2023 +0100 @@ -0,0 +1,60 @@ +/* + * example.c -- libmlk-example entry point + * + * 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/core.h> +#include <mlk/core/err.h> +#include <mlk/core/window.h> + +#include <mlk/ui/ui.h> + +#include <mlk/rpg/rpg.h> + +#include "example.h" +#include "registry.h" + +int +mlk_example_init(const char *name) +{ + assert(name); + + int err; + + if ((err = mlk_core_init("fr.malikania", name)) < 0) + return err; + if ((err = ui_init()) < 0) + return err; + if ((err = rpg_init()) < 0) + return err; + if (window_open(name, MLK_EXAMPLE_W, MLK_EXAMPLE_H) < 0) + return err; + + registry_init(); + + return 0; +} + +void +mlk_example_finish(void) +{ + window_finish(); + rpg_finish(); + ui_finish(); + mlk_core_finish(); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libmlk-example/mlk/example/example.h Mon Feb 27 16:33:33 2023 +0100 @@ -0,0 +1,31 @@ +/* + * example.h -- libmlk-example entry point + * + * 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_EXAMPLE_EXAMPLE_H +#define MLK_EXAMPLE_EXAMPLE_H + +#define MLK_EXAMPLE_W 960 +#define MLK_EXAMPLE_H 540 + +int +mlk_example_init(const char *); + +void +mlk_example_finish(void); + +#endif /* !MLK_EXAMPLE_EXAMPLE_H */
--- a/libmlk-example/mlk/example/registry.c Mon Feb 27 12:49:39 2023 +0100 +++ b/libmlk-example/mlk/example/registry.c Mon Feb 27 16:33:33 2023 +0100 @@ -29,6 +29,7 @@ #include <assets/sounds/fire.h> +#include <assets/sprites/chest.h> #include <assets/sprites/explosion.h> #include <assets/sprites/john-sword.h> #include <assets/sprites/john-walk.h> @@ -36,10 +37,10 @@ #include "registry.h" -struct mlk_texture registry_images[REGISTRY_IMAGE_NUM]; -struct mlk_texture registry_textures[REGISTRY_TEXTURE_NUM]; -struct mlk_sprite registry_sprites[REGISTRY_TEXTURE_NUM]; -struct mlk_sound registry_sounds[REGISTRY_SOUND_NUM]; +struct mlk_texture registry_images[REGISTRY_IMAGE_LAST] = {0}; +struct mlk_texture registry_textures[REGISTRY_TEXTURE_LAST] = {0}; +struct mlk_sprite registry_sprites[REGISTRY_TEXTURE_LAST] = {0}; +struct mlk_sound registry_sounds[REGISTRY_SOUND_LAST] = {0}; #define REGISTRY_IMAGE(i, data) \ { (i), (data), sizeof (data) } @@ -67,7 +68,8 @@ 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) + REGISTRY_TEXTURE(REGISTRY_TEXTURE_BLACK_CAT, assets_images_black_cat, 0, 0), + REGISTRY_TEXTURE(REGISTRY_TEXTURE_CHEST, assets_sprites_chest, 32, 32) }; #define REGISTRY_SOUND(s, data) \ @@ -95,9 +97,12 @@ static void load_textures_and_sprites(void) { + struct mlk_texture *texture; + struct mlk_sprite *sprite; + for (size_t i = 0; i < MLK_UTIL_SIZE(textures); ++i) { - struct mlk_texture *texture = ®istry_textures[textures[i].index]; - struct mlk_sprite *sprite = ®istry_sprites[textures[i].index]; + texture = ®istry_textures[textures[i].index]; + sprite = ®istry_sprites[textures[i].index]; if (mlk_image_openmem(texture, textures[i].data, textures[i].datasz) < 0) mlk_panic();
--- a/libmlk-example/mlk/example/registry.h Mon Feb 27 12:49:39 2023 +0100 +++ b/libmlk-example/mlk/example/registry.h Mon Feb 27 16:33:33 2023 +0100 @@ -38,24 +38,27 @@ REGISTRY_TEXTURE_HAUNTED_WOOD, REGISTRY_TEXTURE_BLACK_CAT, + /* Objects. */ + REGISTRY_TEXTURE_CHEST, + /* Unused.*/ - REGISTRY_TEXTURE_NUM + REGISTRY_TEXTURE_LAST }; enum registry_image { REGISTRY_IMAGE_BATTLE_BACKGROUND, - REGISTRY_IMAGE_NUM + REGISTRY_IMAGE_LAST }; enum registry_sound { REGISTRY_SOUND_FIRE, - REGISTRY_SOUND_NUM + REGISTRY_SOUND_LAST }; -extern struct mlk_texture registry_images[REGISTRY_IMAGE_NUM]; -extern struct mlk_texture registry_textures[REGISTRY_TEXTURE_NUM]; -extern struct mlk_sprite registry_sprites[REGISTRY_TEXTURE_NUM]; -extern struct mlk_sound registry_sounds[REGISTRY_SOUND_NUM]; +extern struct mlk_texture registry_images[REGISTRY_IMAGE_LAST]; +extern struct mlk_texture registry_textures[REGISTRY_TEXTURE_LAST]; +extern struct mlk_sprite registry_sprites[REGISTRY_TEXTURE_LAST]; +extern struct mlk_sound registry_sounds[REGISTRY_SOUND_LAST]; void registry_init(void);