view examples/example-action.c @ 136:30b68089ae70

core: rework actions and a bit of drawables, closes #2492 In the effort of having as less as possible memory allocation in libcore, the usage of actions and drawable no longer copy the original source parameter to let user pass a heap allocated variable or a static storage one. Update predefined drawable and actions to match these new needs.
author David Demelier <markand@malikania.fr>
date Tue, 13 Oct 2020 09:38:44 +0200
parents
children 8b035f7f978a
line wrap: on
line source

/*
 * example-action.c -- example on how to use automatic drawables
 *
 * Copyright (c) 2020 David Demelier <markand@malikania.fr>
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include <assert.h>

#include <core/action.h>
#include <core/clock.h>
#include <core/event.h>
#include <core/image.h>
#include <core/maths.h>
#include <core/message.h>
#include <core/painter.h>
#include <core/panic.h>
#include <core/script.h>
#include <core/sprite.h>
#include <core/sys.h>
#include <core/texture.h>
#include <core/theme.h>
#include <core/util.h>
#include <core/window.h>

#include <assets/sprites/chest.h>
#include <assets/sprites/people.h>

#define W 1280
#define H 720

static struct action_stack stack;

/*
 * Event object for the chest to click on.
 */
static struct {
	struct message msg;
	struct action msg_act;
	int x;
	int y;
	bool opened;
	struct texture image;
	struct sprite sprite;
	struct action event;
} chest = {
	.msg = {
		.text = {
			"100000 pièces.",
			"Te voilà riche sale file de crevette."
		}
	}
};

/*
 * Event object for the guide who ask dialog with you.
 *
 * It is a script spawned upon click like this.
 *
 * [guide.event]
 *       |           spawn
 *   (on click)------------------->[message 1]
 *                                      v
 *                                 [message 2]
 *                                      v
 *                                 [question 3]
 *                   spawn              v
 * [response]<---------------------[check result]
 *     v
 * [message 4/5]
 */
static struct {
	struct {
		struct message msg;
		struct action act;
	} msgs[5];

	/* This is the sprite and action to click on. */
	int x;
	int y;
	struct texture image;
	struct sprite sprite;
	struct action event;

	/* This is the event for the response. */
	struct action response;

	struct script script;
	struct action script_act;
} guide = {
	.msgs = {
		{
			.msg = {
				.text = {
					"Bienvenue dans ce monde Molko."
				}
			},
		},
		{
			.msg = {
				.text = {
					"Penses tu vraiment pouvoir me battre ?"
				}
			}
		},
		{
			.msg = {
				.flags = MESSAGE_QUESTION,
				.text = {
					"Non j'ai la trouille.",
					"Bien sûr, je suis la légende."
				}
			}
		},

		/* In case of NO. */
		{
			.msg = {
				.text = {
					"Poule mouillée."
				}
			}
		},

		/* In case of YES. */
		{
			.msg = {
				.text = {
					"Prépare toi à souffrir."
				}
			}
		}
	}
};

static bool
guide_response_update(struct action *act, unsigned int ticks)
{
	/* Immediately return to get access to end. */
	(void)act;
	(void)ticks;

	return true;
}

static void
guide_response_end(struct action *act)
{
	(void)act;

	/* We add a final response depending on the result. */
	const int index = guide.msgs[2].msg.index + 3;

	message_action(&guide.msgs[index].msg, &guide.msgs[index].act);
	action_stack_add(&stack, &guide.msgs[index].act);
}

static bool
guide_running(void)
{
	return guide.script.actionsz > 0;
}

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);
		script_append(&guide.script, &guide.msgs[i].act);
	}

	/* Reset the message index. */
	guide.msgs[2].msg.index = 0;

	/* This is final action that analyze user input. */
	guide.response.update = guide_response_update;
	guide.response.end = guide_response_end;
	script_append(&guide.script, &guide.response);

	script_action(&guide.script, &guide.script_act);
	action_stack_add(&stack, &guide.script_act);
}

static void
guide_handle(struct action *act, const union event *ev)
{
	(void)act;

	if (guide_running())
		return;

	switch (ev->type) {
	case EVENT_CLICKDOWN:
		if (maths_is_boxed(guide.x, guide.y,
		    guide.sprite.cellw, guide.sprite.cellh,
		    ev->click.x, ev->click.y))
			guide_popup();
		break;
	default:
		break;
	}
}

static void
guide_draw(struct action *act)
{
	(void)act;

	sprite_draw(&guide.sprite, 0, 0, guide.x, guide.y);
}

static void
guide_init(void)
{
	if (!image_openmem(&guide.image, sprites_people, sizeof (sprites_people)))
		panic();

	sprite_init(&guide.sprite, &guide.image, 48, 48);

	/* This is the global guide action. */
	guide.event.handle = guide_handle;
	guide.event.draw = guide_draw;
	guide.x = 200;
	guide.y = 200;
}

static void
chest_handle(struct action *act, const union event *ev)
{
	(void)act;

	if (chest.opened)
		return;

	switch (ev->type) {
	case EVENT_CLICKDOWN:
		if (maths_is_boxed(chest.x, chest.y, chest.sprite.cellw, chest.sprite.cellh,
		    ev->click.x, ev->click.y)) {
			chest.opened = true;
			message_action(&chest.msg, &chest.msg_act);
			action_stack_add(&stack, &chest.msg_act);
		}
	default:
		break;
	}
}

static void
chest_draw(struct action *act)
{
	(void)act;

	const int row = chest.opened ? 3 : 0;

	sprite_draw(&chest.sprite, row, 0, chest.x, chest.y);
}

static void
chest_init(void)
{
	if (!image_openmem(&chest.image, sprites_chest, sizeof (sprites_chest)))
		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 (!sys_init() ||
	    !window_init("Example - Action", W, H) ||
	    !theme_init())
		panic();

	guide_init();
	chest_init();
}

static void
run(void)
{
	struct clock clock = {0};

	clock_start(&clock);
	action_stack_add(&stack, &chest.event);
	action_stack_add(&stack, &guide.event);

	for (;;) {
		unsigned int elapsed = clock_elapsed(&clock);

		clock_start(&clock);

		for (union event ev; event_poll(&ev); ) {
			switch (ev.type) {
			case EVENT_QUIT:
				return;
			default:
				action_stack_handle(&stack, &ev);
				break;
			}
		}

		painter_set_color(0xffffffff);
		painter_clear();
		action_stack_update(&stack, elapsed);
		action_stack_draw(&stack);
		painter_present();

		if ((elapsed = clock_elapsed(&clock)) < 20)
			delay(20 - elapsed);
	}
}

static void
quit(void)
{
	theme_finish();
	window_finish();
	sys_finish();
}

int
main(int argc, char **argv)
{
	(void)argc;
	(void)argv;

	init();
	run();
	quit();

	return 0;
}