changeset 41:3996f873a54b

core: implement walksprite, closes #2455
author David Demelier <markand@malikania.fr>
date Wed, 15 Jan 2020 13:11:47 +0100
parents 2a087210c0b8
children 22a09a5ee476
files Makefile src/main.c src/map.c src/map.h src/painter.c src/painter.h src/walksprite.c src/walksprite.h
diffstat 8 files changed, 330 insertions(+), 3 deletions(-) [+]
line wrap: on
line diff
--- a/Makefile	Tue Jan 14 13:40:26 2020 +0100
+++ b/Makefile	Wed Jan 15 13:11:47 2020 +0100
@@ -35,6 +35,7 @@
                 src/sys.c \
                 src/texture.c \
                 src/util.c \
+                src/walksprite.c \
                 src/window.c
 OBJS=           ${SRCS:.c=.o}
 DEPS=           ${SRCS:.c=.d}
--- a/src/main.c	Tue Jan 14 13:40:26 2020 +0100
+++ b/src/main.c	Wed Jan 15 13:11:47 2020 +0100
@@ -23,7 +23,10 @@
 #include "error.h"
 #include "event.h"
 #include "painter.h"
+#include "image.h"
+#include "map.h"
 #include "sys.h"
+#include "walksprite.h"
 #include "window.h"
 #include "util.h"
 
@@ -34,28 +37,94 @@
 	(void)argv;
 
 	struct clock clock;
+	struct map map;
+	struct texture *guy;
+	struct sprite sprite;
+	struct walksprite ws;
 
 	if (!sys_init())
 		error_fatal();
 	if (!window_init("Molko's Adventure", 1024, 576))
 		error_fatal();
+	if (!map_open(&map, "world.json"))
+		error_fatal();
+	if (!(guy = image_openf(sys_datapath("sprites/test-walk.png"))))
+		error_fatal();
 
+	sprite_init(&sprite, guy, 48, 48);
+	walksprite_init(&ws, &sprite, 200);
 	clock_start(&clock);
+	map_repaint(&map);
+
+	int guy_x = 10;
+	int guy_y = 10;
+	int guy_dx = 0;
+	int guy_dy = 0;
+
+	int dx = 0;
+	int dy = 0;
+	int orientation = 0;
 
 	for (;;) {
-		/*uint64_t elapsed = clock_elapsed(&clock);*/
+		uint64_t elapsed = clock_elapsed(&clock);
 		union event ev;
 
+		clock_start(&clock);
+
 		while (event_poll(&ev)) {
 			switch (ev.type) {
 			case EVENT_QUIT:
 				return 0;
+			case EVENT_KEYDOWN:
+				switch (ev.key.key) {
+				case KEY_UP:
+					orientation = 0;
+					dy = -5;
+					break;
+				case KEY_RIGHT:
+					orientation = 2;
+					dx = 5;
+					break;
+				case KEY_DOWN:
+					orientation = 4;
+					dy = 5;
+					break;
+				case KEY_LEFT:
+					orientation = 6;
+					dx = -5;
+					break;
+				default:
+					break;
+				}
+				break;
+			case EVENT_KEYUP:
+				switch (ev.key.key) {
+				case KEY_DOWN:
+					dy = 0;
+					break;
+				case KEY_UP:
+					dy = 0;
+					break;
+				case KEY_LEFT:
+					dx = 0;
+					break;
+				case KEY_RIGHT:
+					dx = 0;
+					break;
+				default:
+					break;
+				}
+				break;
 			default:
 				break;
 			}
 		}
 
+		map_move(&map, map.x + dx, map.y + dy);
+		walksprite_update(&ws, elapsed);
 		painter_clear();
+		map_draw(&map);
+		walksprite_draw(&ws, orientation, 100, 100);
 		painter_present();
 		delay(50);
 	}
--- a/src/map.c	Tue Jan 14 13:40:26 2020 +0100
+++ b/src/map.c	Wed Jan 15 13:11:47 2020 +0100
@@ -21,10 +21,13 @@
 #include <stdlib.h>
 #include <string.h>
 
+#include "error.h"
+#include "error_p.h"
 #include "map.h"
 #include "image.h"
 #include "texture.h"
 #include "sprite.h"
+#include "painter.h"
 
 #include <SDL.h>
 
@@ -143,14 +146,56 @@
 
 	fclose(fp);
 
+	if (map->width == 0 || map->tilewidth == 0)
+		return error_printf("map width is null");
+	if (map->height == 0 || map->tileheight == 0)
+		return error_printf("map height is null");
+
+	size_t pw = map->width * map->tilewidth;
+	size_t ph = map->height * map->tileheight;
+
+	if (!(map->picture = texture_new(pw, ph)))
+		return error_sdl();
+
 	return true;
 }
 
 void
 map_draw(struct map *map)
 {
+	/* TODO: coordinates here */
+	/* TODO: remove window size here */
+	texture_draw_ex(
+		map->picture,
+		map->x,
+		map->y,
+		1024,
+		576,
+		0,
+		0,
+		1024,
+		576,
+		0
+	);
+}
+
+void
+map_repaint(struct map *map)
+{
+	struct texture *old;
+
+	old = painter_get_target();
+	painter_set_target(map->picture);
 	draw_layer(map, &map->layers[0]);
 	draw_layer(map, &map->layers[1]);
+	painter_set_target(old);
+}
+
+void
+map_view(struct map *map, uint64_t x, uint64_t y)
+{
+	map->x = x;
+	map->y = y;
 }
 
 void
@@ -160,6 +205,8 @@
 
 	if (map->tileset)
 		texture_close(map->tileset);
+	if (map->picture)
+		texture_close(map->picture);
 
 	free(map->layers[0].tiles);
 	free(map->layers[1].tiles);
--- a/src/map.h	Tue Jan 14 13:40:26 2020 +0100
+++ b/src/map.h	Wed Jan 15 13:11:47 2020 +0100
@@ -49,7 +49,10 @@
 struct map {
 	char title[MAP_TITLE_MAX];      /*!< (RW) The map title */
 	struct texture *tileset;        /*!< (RW) Tileset to use */
+	struct texture *picture;        /*!< (RO) Map drawn into a picture */
 	struct sprite sprite;           /*!< (RO) Sprite to render */
+	uint64_t x;
+	uint64_t y;
 	uint16_t width;                 /*!< (RO) Map width in cells */
 	uint16_t height;                /*!< (RO) Map height in cells */
 	uint8_t tilewidth;              /*!< (RO) Pixels per cell (width) */
@@ -79,6 +82,27 @@
 map_draw(struct map *map);
 
 /**
+ * Force map repaint on its texture.
+ *
+ * \pre map != NULL
+ * \param map the map to repaint
+ * \warning This function does not render anything on the screen
+ */
+void
+map_repaint(struct map *map);
+
+/**
+ * Move the origin of the picture rect to view.
+ *
+ * \param map the map
+ * \param x the x coordinate
+ * \param y the y coordinate
+ * \note This function may adjust the coordinates if needed
+ */
+void
+map_view(struct map *map, uint64_t x, uint64_t y);
+
+/**
  * Close the map and its resources.
  *
  * \pre map != NULL
--- a/src/painter.c	Tue Jan 14 13:40:26 2020 +0100
+++ b/src/painter.c	Wed Jan 15 13:11:47 2020 +0100
@@ -21,9 +21,18 @@
 #include "texture_p.h"
 #include "window_p.h"
 
+static struct texture *renderer;
+
+struct texture *
+painter_get_target(void)
+{
+	return renderer;
+}
+
 void
-painter_use(struct texture *tex)
+painter_set_target(struct texture *tex)
 {
+	renderer = tex;
 	SDL_SetRenderTarget(win.renderer, tex ? tex->handle : NULL);
 }
 
--- a/src/painter.h	Tue Jan 14 13:40:26 2020 +0100
+++ b/src/painter.h	Wed Jan 15 13:11:47 2020 +0100
@@ -30,14 +30,27 @@
 struct texture;
 
 /**
+ * Give the current texture being used for rendering, maybe NULL if the
+ * rendering is on root.
+ *
+ * \return texture or NULL
+ */
+struct texture *
+painter_get_target(void);
+
+/**
  * Set the rendering context to the given texture.
  *
+ * Since this function change an internal global variable it is strongly
+ * encouraged to store a local backup of the current texture using \a
+ * painter_get_target and call this function with it afterwards.
+ *
  * If texture is NULL, use default context aka the window.
  *
  * \param tex the texture
  */
 void
-painter_use(struct texture *tex);
+painter_set_target(struct texture *tex);
 
 /**
  * Get the current drawing color.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/walksprite.c	Wed Jan 15 13:11:47 2020 +0100
@@ -0,0 +1,60 @@
+/*
+ * walksprite.c -- sprite designed for walking entities
+ *
+ * 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 <string.h>
+
+#include "walksprite.h"
+#include "sprite.h"
+
+void
+walksprite_init(struct walksprite *ws, struct sprite *sprite, uint16_t delay)
+{
+	assert(ws);
+	assert(sprite);
+
+	memset(ws, 0, sizeof (struct walksprite));
+	ws->sprite = sprite;
+	ws->delay = delay;
+}
+
+void
+walksprite_update(struct walksprite *ws, unsigned ticks)
+{
+	assert(ws);
+
+	ws->elapsed += ticks;
+
+	if (ws->elapsed >= ws->delay) {
+		ws->index += 1;
+
+		if (ws->index >= ws->sprite->ncols)
+			ws->index = 0;
+
+		ws->elapsed = 0;
+	}
+}
+
+void
+walksprite_draw(struct walksprite *ws, uint8_t orientation, int x, int y)
+{
+	assert(ws);
+	assert(orientation < 8);
+
+	sprite_draw(ws->sprite, orientation, ws->index, x, y);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/walksprite.h	Wed Jan 15 13:11:47 2020 +0100
@@ -0,0 +1,104 @@
+/*
+ * walksprite.h -- sprite designed for walking entities
+ *
+ * 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_WALKSPRITE_H
+#define MOLKO_WALKSPRITE_H
+
+/**
+ * \file walksprite.h
+ * \brief Sprite designed for walking entities.
+ */
+
+#include <stdint.h>
+
+struct sprite;
+
+/**
+ * \brief Sprite designed for walking entities.
+ *
+ * This structure works with sprite images that are defined as using the
+ * following conventions:
+ *
+ * ```
+ * 7   0   1
+ *   ↖ ↑ ↗
+ * 6 ←   → 2
+ *   ↙ ↓ ↘
+ * 5   4   3
+ * ```
+ *
+ * Where numbers define row in the sprite according to the character
+ * orientation. In other terms, your image sprite should look like this:
+ *
+ * ```
+ * row columns in your image
+ * ---|---------------------
+ *  0 | ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
+ *  1 | ↗↗↗↗↗↗↗↗↗↗↗↗↗↗↗↗↗↗↗↗
+ *  2 | →→→→→→→→→→→→→→→→→→→→
+ *  3 | ↘↘↘↘↘↘↘↘↘↘↘↘↘↘↘↘↘↘↘↘
+ *  4 | ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
+ *  5 | ↙↙↙↙↙↙↙↙↙↙↙↙↙↙↙↙↙↙↙↙
+ *  6 | ←←←←←←←←←←←←←←←←←←←←
+ *  7 | ↖↖↖↖↖↖↖↖↖↖↖↖↖↖↖↖↖↖↖↖
+ * ```
+ */
+struct walksprite {
+	struct sprite *sprite;  /*!< (RW) The sprite to use */
+	uint16_t delay;         /*!< (RW) The delay between frames */
+	uint8_t index;          /*!< (RO) Current column index */
+	uint16_t elapsed;       /*!< (RO) Elapsed time since last frame */
+};
+
+/**
+ * Initialize the walking sprite.
+ *
+ * \pre ws != NULL
+ * \pre sprite != NULL
+ * \param ws the walking sprite
+ * \param sprite the sprite to use
+ * \param delay the delay between sprites
+ */
+void
+walksprite_init(struct walksprite *ws, struct sprite *sprite, uint16_t delay);
+
+/**
+ * Update the walking sprite
+ *
+ * \pre ws != NULL
+ * \param ws the walking sprite
+ * \param ticks the number of milliseconds between last frame
+ */
+void
+walksprite_update(struct walksprite *ws, unsigned ticks);
+
+/**
+ * Draw the appropriate sprite cell to the screen according to the orientation
+ * given.
+ *
+ * \pre ws != NULL
+ * \pre orientation < 8
+ * \param ws the walking sprite
+ * \param orientation the orientation (or the row between 0 and 7)
+ * \param x the x coordinate
+ * \param y the y coordinate
+ */
+void
+walksprite_draw(struct walksprite *ws, uint8_t orientation, int x, int y);
+
+#endif /* !MOLKO_WALKSPRITE_H */