changeset 80:05ffbcdee585

adventure: create main menu, closes #2482 @4h
author David Demelier <markand@malikania.fr>
date Sat, 15 Feb 2020 12:38:25 +0100
parents 8f95462ac5f8
children e8fcc2a1d250
files Makefile assets/fonts/pirata-one.ttf assets/fonts/teutonic1.ttf src/adventure/main.c src/adventure/mainmenu_state.c src/adventure/mainmenu_state.h src/adventure/splashscreen_state.c src/adventure/splashscreen_state.h src/core/game.c src/core/game.h src/core/map_state.h
diffstat 11 files changed, 298 insertions(+), 81 deletions(-) [+]
line wrap: on
line diff
--- a/Makefile	Sun Feb 02 15:48:36 2020 +0100
+++ b/Makefile	Sat Feb 15 12:38:25 2020 +0100
@@ -50,7 +50,8 @@
 CORE_DEPS=      ${CORE_SRCS:.c=.d}
 
 ADV_SRCS=       src/adventure/main.c \
-                src/adventure/splashscreen_state.c
+                src/adventure/splashscreen_state.c \
+                src/adventure/mainmenu_state.c
 ADV_OBJS=       ${ADV_SRCS:.c=.o}
 ADV_DEPS=       ${ADV_SRCS:.c=.d}
 
Binary file assets/fonts/pirata-one.ttf has changed
Binary file assets/fonts/teutonic1.ttf has changed
--- a/src/adventure/main.c	Sun Feb 02 15:48:36 2020 +0100
+++ b/src/adventure/main.c	Sat Feb 15 12:38:25 2020 +0100
@@ -56,22 +56,12 @@
 }
 
 static void
-myresult(struct action *a)
-{
-	struct message *msg = a->data;
-
-	printf("selected: %d\n", msg->index);
-}
-
-static void
 run(void)
 {
 	union event ev;
 	struct clock clock;
 	struct font *font;
 	struct texture *frame;
-	struct script sc;
-	struct action ac;
 
 	if (!(font = font_openf(sys_datapath("fonts/DejaVuSans.ttf"), 15)))
 		error_fatal();
@@ -80,50 +70,11 @@
 	if (!(frame = image_openf(sys_datapath("images/message.png"))))
 		error_fatal();
 
-	struct message msg = {
-		.text = {
-			"Flip a coin.",
-			"Try your best my friend."
-		},
-		.colors = {
-			0xd9caddff,
-			0x94d5ffff
-		},
-		.font = font,
-		.frame = frame,
-		.flags = MESSAGE_QUESTION
-	};
-
 	debug_options.enable = true;
 
 	clock_start(&clock);
-	script_init(&sc);
 
-	/* Wait first. */
-	struct wait w = { .delay = 5000 };
-
-	wait_action(&w, &ac);
-	script_append(&sc, &ac);
-
-	/* Inhibit input. */
-	inhibit_action(INHIBIT_STATE_INPUT, &ac);
-	script_append(&sc, &ac);
-
-	message_start(&msg);
-	message_action(&msg, &ac);
-	ac.end = myresult;
-	script_append(&sc, &ac);
-
-	/* Put it back. */
-	inhibit_action(INHIBIT_NONE, &ac);
-	script_append(&sc, &ac);
-
-	script_start(&sc);
-	script_action(&sc, &ac);
-
-	game_add_action(&ac);
-
-	for (;;) {
+	while (game.state) {
 		unsigned int elapsed = clock_elapsed(&clock);
 
 		clock_start(&clock);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/adventure/mainmenu_state.c	Sat Feb 15 12:38:25 2020 +0100
@@ -0,0 +1,210 @@
+/*
+ * mainmenu_state.c -- game main menu
+ *
+ * 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 <stdlib.h>
+#include <string.h>
+
+#include <error.h>
+#include <event.h>
+#include <font.h>
+#include <game.h>
+#include <image.h>
+#include <map_state.h>
+#include <painter.h>
+#include <state.h>
+#include <sys.h>
+#include <texture.h>
+#include <window.h>
+
+#include "mainmenu_state.h"
+#include "splashscreen_state.h"
+
+#define SPEED 120
+#define SEC   1000
+
+enum substate {
+	SUBSTATE_MOVING,
+	SUBSTATE_WAITING
+};
+
+static int x;
+static int y;
+static unsigned int selection;
+static int destination;
+static enum substate substate;
+
+/* Menu items. */
+static struct {
+	struct texture *texture;
+	int x;
+	int y;
+} items[3];
+
+static void
+enter(void)
+{
+	struct font *font = font_openf(sys_datapath("fonts/pirata-one.ttf"), 30);
+
+	if (!font)
+		error_fatal();
+
+	substate = SUBSTATE_MOVING;
+	x = splashscreen_state_data.x;
+	y = splashscreen_state_data.y;
+	destination = window_height() / 4;
+
+	/* TODO: change continue color if no game exists. */
+	items[0].texture = font_render(font, "New game", 0x000000ff);
+	items[0].x = (window_width() / 2) - (texture_width(items[0].texture) / 2);
+	items[0].y = window_height() * 0.75;
+
+	items[1].texture = font_render(font, "Continue", 0x000000ff);
+	items[1].x = items[0].x;
+	items[1].y = items[0].y + texture_height(items[0].texture);
+
+	items[2].texture = font_render(font, "Quit", 0x000000ff);
+	items[2].x = items[0].x;
+	items[2].y = items[1].y + texture_height(items[1].texture);
+}
+
+static void
+new(void)
+{
+	struct map map;
+	struct texture *image;
+
+	/* Prepare map. */
+	if (!map_open(&map, sys_datapath("maps/test.map")))
+		error_fatal();
+
+	memcpy(&map_state_data.map.map, &map, sizeof (map));
+
+	/* Prepare image and sprite. */
+	if (!(image = image_openf(sys_datapath("sprites/test-walk.png"))))
+		error_fatal();
+
+	sprite_init(&map_state_data.player.sprite, image, 48, 48);
+	game_switch(&map_state, false);
+}
+
+static void
+resume(void)
+{
+}
+
+static void
+quit(void)
+{
+	game_quit();
+}
+
+static void
+perform(void)
+{
+	assert(selection < 3);
+
+	static void (*handlers[])(void) = {
+		[0] = new,
+		[1] = resume,
+		[2] = quit
+	};
+
+	handlers[selection]();
+}
+
+static void
+handle(const union event *event)
+{
+	if (substate != SUBSTATE_WAITING)
+		return;
+
+	switch (event->type) {
+	case EVENT_KEYDOWN:
+		switch (event->key.key) {
+		case KEY_UP:
+			selection = selection == 0 ? 2 : selection - 1;
+			break;
+		case KEY_DOWN:
+			selection = (selection + 1) % 3;
+			break;
+		case KEY_ENTER:
+			perform();
+		default:
+			break;
+		}
+		break;
+	default:
+		break;
+	}
+}
+
+static void
+update(unsigned int ticks)
+{
+	switch (substate) {
+	case SUBSTATE_MOVING:
+		y -= SPEED * ticks / SEC;
+
+		if (y <= destination) {
+			y = destination;
+			substate = SUBSTATE_WAITING;
+		}
+
+		break;
+	default:
+		break;
+	}
+}
+
+static void
+draw(void)
+{
+	painter_set_color(0xffffffff);
+	painter_clear();
+	texture_draw(splashscreen_state_data.text, x, y);
+
+	if (substate == SUBSTATE_WAITING) {
+		texture_draw(items[0].texture, items[0].x, items[0].y);
+		texture_draw(items[1].texture, items[1].x, items[1].y);
+		texture_draw(items[2].texture, items[2].x, items[2].y);
+
+		/* Selection cursor. */
+
+		/* TODO: a sword here. */
+		painter_set_color(0x000000ff);
+		painter_draw_rectangle(true, items[selection].x - 30,
+		    items[selection].y + 11, 15, 15);
+	}
+}
+
+static void
+leave(void)
+{
+	texture_close(items[0].texture);
+	texture_close(items[1].texture);
+	memset(items, 0, sizeof (items));
+}
+
+struct state mainmenu_state = {
+	.enter = enter,
+	.handle = handle,
+	.update = update,
+	.draw = draw,
+	.leave = leave
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/adventure/mainmenu_state.h	Sat Feb 15 12:38:25 2020 +0100
@@ -0,0 +1,32 @@
+/*
+ * mainmenu_state.h -- game main menu
+ *
+ * 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 MOLKO_MAINMENU_STATE_H
+#define MOLKO_MAINMENU_STATE_H
+
+/**
+ * \file mainmenu_state.h
+ * \brief Game main menu.
+ */
+
+/**
+ * \brief Main menu state.
+ */
+extern struct state mainmenu_state;
+
+#endif /* !MOLKO_MAINMENU_STATE_H */
--- a/src/adventure/splashscreen_state.c	Sun Feb 02 15:48:36 2020 +0100
+++ b/src/adventure/splashscreen_state.c	Sat Feb 15 12:38:25 2020 +0100
@@ -23,40 +23,42 @@
 #include "map.h"
 #include "map_state.h"
 #include "painter.h"
-#include "splashscreen_state.h"
 #include "state.h"
 #include "sys.h"
 #include "texture.h"
 #include "window.h"
 
-#define DELAY 3000
+#include "splashscreen_state.h"
+#include "mainmenu_state.h"
 
-static unsigned int elapsed;
-static struct font *font;
-static struct texture *text;
-static int x;
-static int y;
+#define DELAY 2000
+
+struct splashscreen_state_data splashscreen_state_data;
 
 static void
 enter(void)
 {
-	if (!(font = font_openf(sys_datapath("fonts/knights-quest.ttf"), 160)))
+	struct font *font;
+
+	if (!(font = font_openf(sys_datapath("fonts/teutonic1.ttf"), 130)))
 		error_fatal();
-	if (!(text = font_render(font, "Molko's Adventure", 0x000000ff)))
+	if (!(splashscreen_state_data.text = font_render(font, "Molko's Adventure", 0x000000ff)))
 		error_fatal();
 
 	/* Compute position. */
-	const unsigned int w = texture_width(text);
-	const unsigned int h = texture_height(text);
+	const unsigned int w = texture_width(splashscreen_state_data.text);
+	const unsigned int h = texture_height(splashscreen_state_data.text);
 
-	x = (window_width() / 2) - (w / 2);
-	y = (window_height() / 2) - (h / 2) - 100;
+	splashscreen_state_data.x = (window_width() / 2) - (w / 2);
+	splashscreen_state_data.y = (window_height() / 2) - (h / 2);
+
+	font_close(font);
 }
 
 static void
 leave(void)
 {
-	font_close(font);
+	/* We don't delete the texture because it is used by mainmenu_state. */
 }
 
 static void
@@ -68,21 +70,10 @@
 static void
 update(unsigned int ticks)
 {
-	elapsed += ticks;
-
-	/* TODO: change this once map is done. */
-	if (elapsed >= DELAY) {
-		/* TODO: this will be removed too. */
-		static struct texture *image;
+	splashscreen_state_data.elapsed += ticks;
 
-		if (!map_open(&map_state_data.map.map, sys_datapath("maps/test.map")))
-			error_fatal();
-		if (!(image = image_openf(sys_datapath("sprites/test-walk.png"))))
-			error_fatal();
-
-		sprite_init(&map_state_data.player.sprite, image, 48, 48);
-		game_switch(&map_state, false);
-	}
+	if (splashscreen_state_data.elapsed >= DELAY)
+		game_switch(&mainmenu_state, false);
 }
 
 static void
@@ -90,7 +81,9 @@
 {
 	painter_set_color(0xffffffff);
 	painter_clear();
-	texture_draw(text, x, y);
+	texture_draw(splashscreen_state_data.text,
+		splashscreen_state_data.x,
+		splashscreen_state_data.y);
 }
 
 struct state splashscreen_state = {
--- a/src/adventure/splashscreen_state.h	Sun Feb 02 15:48:36 2020 +0100
+++ b/src/adventure/splashscreen_state.h	Sat Feb 15 12:38:25 2020 +0100
@@ -24,6 +24,18 @@
  * \brief Splash screen state.
  */
 
+struct texture;
+
+/**
+ * \brief Data for splashscreen.
+ */
+extern struct splashscreen_state_data {
+	struct texture *text;           /*!< (RW) Texture for the text. */
+	int x;                          /*!< (RW) Position in x. */
+	int y;                          /*!< (RW) Position in y. */
+	unsigned int elapsed;           /*!< (RW) Time elapsed. */
+} splashscreen_state_data;              /*!< (RW) Global state data. */
+
 /**
  * \brief Splash screen state.
  */
--- a/src/core/game.c	Sun Feb 02 15:48:36 2020 +0100
+++ b/src/core/game.c	Sat Feb 15 12:38:25 2020 +0100
@@ -157,3 +157,12 @@
 	if ((pos = find_empty_action()))
 		memcpy(pos, action, sizeof (struct action));
 }
+
+void
+game_quit(void)
+{
+	if (game.state && game.state->leave)
+		game.state->leave();
+
+	game.state = NULL;
+}
--- a/src/core/game.h	Sun Feb 02 15:48:36 2020 +0100
+++ b/src/core/game.h	Sat Feb 15 12:38:25 2020 +0100
@@ -106,4 +106,13 @@
 void
 game_add_action(const struct action *action);
 
+/**
+ * Stop the game.
+ *
+ * This will effectively stop the current state but the main loop may continue
+ * until it has completed.
+ */
+void
+game_quit(void);
+
 #endif /* !MOLKO_GAME_H */
--- a/src/core/map_state.h	Sun Feb 02 15:48:36 2020 +0100
+++ b/src/core/map_state.h	Sat Feb 15 12:38:25 2020 +0100
@@ -38,7 +38,7 @@
 	 * Map properties.
 	 */
 	struct {
-		struct map map;         /*!< (RO) The map definition */
+		struct map map;         /*!< (RW) The map definition */
 		unsigned int w;         /*!< (RO) Map width */
 		unsigned int h;         /*!< (RO) Map height */
 	} map;