changeset 57:9f6267843815

core: implement basic actions, closes #2463 @1h
author David Demelier <markand@malikania.fr>
date Tue, 21 Jan 2020 12:28:26 +0100
parents 43d1102a367e
children d7d88ac30611
files src/action.h src/game.c src/game.h src/main.c src/splashscreen.c
diffstat 5 files changed, 210 insertions(+), 27 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/action.h	Tue Jan 21 12:28:26 2020 +0100
@@ -0,0 +1,88 @@
+/*
+ * action.h -- action states
+ *
+ * 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.
+ */
+
+#ifndef ACTION_H
+#define ACTION_H
+
+/**
+ * \file action.h
+ * \brief Action states.
+ */
+
+#include <stdbool.h>
+
+union event;
+
+/**
+ * \brief Action flags.
+ */
+enum action_flags {
+	ACTION_NONE,                            /*!< No flags */
+	ACTION_AUTO_LEAVE       = (1 << 0)      /*!< Action is removed on state change */
+};
+
+/**
+ * \brief Action structure.
+ */
+struct action {
+	/**
+	 * (RW)
+	 *
+	 * Optional flags.
+	 */
+	enum action_flags flags;
+
+	/**
+	 * (RW)
+	 *
+	 * Arbitrary user data.
+	 */
+	void *data;
+
+	/**
+	 * (RW)
+	 *
+	 * Handle event.
+	 */
+	void (*handle)(struct action *, const union event *event);
+
+	/**
+	 * (RW)
+	 *
+	 * Update the action.
+	 *
+	 * If returns true, the action is removed.
+	 */
+	bool (*update)(struct action *, unsigned int);
+
+	/**
+	 * (RW)
+	 *
+	 * Draw the aciton.
+	 */
+	void (*draw)(struct action *);
+
+	/**
+	 * (RW)
+	 *
+	 * Close the action before removal.
+	 */
+	void (*finish)(struct action *);
+};
+
+#endif /* !ACTION_H*/
--- a/src/game.c	Tue Jan 21 12:26:49 2020 +0100
+++ b/src/game.c	Tue Jan 21 12:28:26 2020 +0100
@@ -18,6 +18,7 @@
 
 #include <assert.h>
 #include <stddef.h>
+#include <string.h>
 
 #include "game.h"
 #include "state.h"
@@ -27,19 +28,81 @@
 	.state_next = NULL
 };
 
+static struct action *
+find_empty_action(void)
+{
+	static struct action null;
+
+	for (size_t i = 0; i < GAME_ACTIONS_MAX; ++i)
+		if (memcmp(&game.actions[i], &null, sizeof (struct action)) == 0)
+			return &game.actions[i];
+
+	return NULL;
+}
+
+static void
+clear_actions(void)
+{
+	for (size_t i = 0; i < GAME_ACTIONS_MAX; ++i) {
+		struct action *a = &game.actions[i];
+
+		/* These actions are removed on state change. */
+		if (a->flags & ACTION_AUTO_LEAVE) {
+			if (a->finish)
+				a->finish(a);
+
+			memset(a, 0, sizeof (struct action));
+		}
+	}
+}
+
+static void
+handle_actions(const union event *event)
+{
+	for (size_t i = 0; i < GAME_ACTIONS_MAX; ++i)
+		if (game.actions[i].handle)
+			game.actions[i].handle(&game.actions[i], event);
+}
+
+static void
+update_actions(unsigned int ticks)
+{
+	for (size_t i = 0; i < GAME_ACTIONS_MAX; ++i) {
+		struct action *a = &game.actions[i];
+
+		if (!a->update)
+			continue;
+
+		if (a->update(a, ticks)) {
+			if (a->finish)
+				a->finish(a);
+
+			memset(&game.actions[i], 0, sizeof (struct action));
+		}
+	}
+}
+
 void
-game_switch(struct state *state)
+game_switch(struct state *state, bool quick)
 {
 	assert(state);
 
-	game.state_next = state;
+	if (quick) {
+		game.state = state;
+		game.state->enter();
+	} else
+		game.state_next = state;
 }
 
 void
 game_handle(const union event *event)
 {
+	assert(event);
+
 	if (game.state)
 		game.state->handle(event);
+
+	handle_actions(event);
 }
 
 void
@@ -54,10 +117,15 @@
 		game.state = game.state_next;
 		game.state->enter();
 		game.state_next = NULL;
+
+		/* Remove any actions that must be deleted. */
+		clear_actions();
 	}
 
 	if (game.state)
 		game.state->update(ticks);
+
+	update_actions(ticks);
 }
 
 void
@@ -66,3 +134,14 @@
 	if (game.state)
 		game.state->draw();
 }
+
+void
+game_add_action(const struct action *action)
+{
+	assert(action);
+
+	struct action *pos;
+
+	if ((pos = find_empty_action()))
+		memcpy(pos, action, sizeof (struct action));
+}
--- a/src/game.h	Tue Jan 21 12:26:49 2020 +0100
+++ b/src/game.h	Tue Jan 21 12:28:26 2020 +0100
@@ -24,6 +24,15 @@
  * \brief Main game object.
  */
 
+#include <stdbool.h>
+
+#include "action.h"
+
+/**
+ * \brief Max number of actions allowed at the same time.
+ */
+#define GAME_ACTIONS_MAX 32
+
 struct state;
 
 union event;
@@ -32,9 +41,12 @@
  * \brief Main game object.
  */
 struct game {
-	/* Game states */
+	/* Game states. */
 	struct state *state;            /*!< (RO) Current state  */
 	struct state *state_next;       /*!< (RO) Next state */
+
+	/** Array of actions. */
+	struct action actions[GAME_ACTIONS_MAX];
 };
 
 /**
@@ -47,11 +59,14 @@
  *
  * This function will only update state after the next \a game_update call.
  *
+ * If quick is true, change state immediately.
+ *
  * \pre state != NULL
  * \param state the new state
+ * \param quick quickly change the state
  */
 void
-game_switch(struct state *state);
+game_switch(struct state *state, bool quick);
 
 /**
  * Handle input event.
@@ -75,4 +90,16 @@
 void
 game_draw(void);
 
+/**
+ * Add an action to the game.
+ *
+ * If there are no room for the action, action is discarded. Make sure to not
+ * exceed the limit GAME_ACTIONS_MAX.
+ *
+ * \pre action != NULL
+ * \param action the action to copy
+ */
+void
+game_add_action(const struct action *action);
+
 #endif /* !MOLKO_GAME_H */
--- a/src/main.c	Tue Jan 21 12:26:49 2020 +0100
+++ b/src/main.c	Tue Jan 21 12:28:26 2020 +0100
@@ -16,6 +16,8 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
+#include <stdio.h>
+
 #include "clock.h"
 #include "error.h"
 #include "event.h"
@@ -35,25 +37,13 @@
 static void
 init(void)
 {
-	struct texture *image;
-
 	if (!sys_init())
 		error_fatal();
 	if (!window_init("Molko's Adventure", WINDOW_WIDTH, WINDOW_HEIGHT))
 		error_fatal();
 
-	image = image_openf(sys_datapath("sprites/test-walk.png"));
-
-	if (!image)
-		error_fatal();
-	if (!map_open(&map_state_data.map.map, sys_datapath("maps/test.map")))
-		error_fatal();
-
-	sprite_init(&map_state_data.player.sprite, image, 48, 48);
-	game_switch(&map_state);
-
 	/* Default state is splash screen */
-	//game_switch(&splashscreen_state);
+	game_switch(&splashscreen_state, true);
 }
 
 static void
--- a/src/splashscreen.c	Tue Jan 21 12:26:49 2020 +0100
+++ b/src/splashscreen.c	Tue Jan 21 12:28:26 2020 +0100
@@ -18,15 +18,16 @@
 
 #include "error.h"
 #include "font.h"
+#include "game.h"
+#include "image.h"
+#include "map.h"
+#include "map_state.h"
 #include "painter.h"
 #include "splashscreen.h"
-#include "map_state.h"
-#include "image.h"
 #include "state.h"
 #include "sys.h"
 #include "texture.h"
 #include "window.h"
-#include "map.h"
 
 #define DELAY 3000
 
@@ -72,17 +73,15 @@
 	/* TODO: change this once map is done. */
 	if (elapsed >= DELAY) {
 		/* TODO: this will be removed too. */
-		static struct map map;
-		static struct texture *player_texture;
-		static struct sprite player_sprite;
+		static struct texture *image;
 
-		if (!map_open(&map, sys_datapath("maps/test.map")))
+		if (!map_open(&map_state_data.map.map, sys_datapath("maps/test.map")))
 			error_fatal();
-		if (!(player_texture = image_openf(sys_datapath("sprites/test-walk.png"))))
+		if (!(image = image_openf(sys_datapath("sprites/test-walk.png"))))
 			error_fatal();
 
-		sprite_init(&player_sprite, player_texture, 48, 48);
-		//map_state_start(&map, &player_sprite);
+		sprite_init(&map_state_data.player.sprite, image, 48, 48);
+		game_switch(&map_state, false);
 	}
 }