diff librpg/rpg/map.c @ 215:64f24b482722

rpg: implement tilesets separately, closes #2515 @4h While here: - Add CMake macros, - Update maps, - Add more tests.
author David Demelier <markand@malikania.fr>
date Tue, 17 Nov 2020 20:08:42 +0100
parents ddfe0a211169
children 33ddbe30440e
line wrap: on
line diff
--- a/librpg/rpg/map.c	Mon Nov 16 21:57:23 2020 +0100
+++ b/librpg/rpg/map.c	Tue Nov 17 20:08:42 2020 +0100
@@ -27,7 +27,6 @@
 #include <core/maths.h>
 #include <core/painter.h>
 #include <core/sprite.h>
-#include <core/state.h>
 #include <core/sys.h>
 #include <core/texture.h>
 #include <core/window.h>
@@ -35,6 +34,7 @@
 #include <ui/debug.h>
 
 #include "map.h"
+#include "tileset.h"
 
 /*
  * This is the speed the player moves on the map.
@@ -52,6 +52,9 @@
 #define MARGIN_WIDTH    80
 #define MARGIN_HEIGHT   80
 
+#define WIDTH(map)      ((map)->columns * (map)->tileset->sprite->cellw)
+#define HEIGHT(map)     ((map)->rows * (map)->tileset->sprite->cellh)
+
 /*
  * This structure defines the possible movement of the player as flags since
  * it's possible to make diagonal movements.
@@ -130,20 +133,18 @@
 
 	if (map->view_x < 0)
 		map->view_x = 0;
-	else if ((unsigned int)map->view_x > map->real_w - map->view_w)
-		map->view_x = map->real_w - map->view_w;
+	else if ((unsigned int)map->view_x > WIDTH(map) - map->view_w)
+		map->view_x = WIDTH(map) - map->view_w;
 
 	if (map->view_y < 0)
 		map->view_y = 0;
-	else if ((unsigned int)map->view_y > map->real_h - map->view_h)
-		map->view_y = map->real_h - map->view_h;
+	else if ((unsigned int)map->view_y > HEIGHT(map) - map->view_h)
+		map->view_y = HEIGHT(map) - map->view_h;
 }
 
 static void
 init(struct map *map)
 {
-	map_repaint(map);
-
 	/* Adjust view. */
 	map->view_w = window.w;
 	map->view_h = window.h;
@@ -188,7 +189,6 @@
 	switch (event->key.key) {
 	case KEY_TAB:
 		map->flags ^= MAP_FLAGS_SHOW_GRID | MAP_FLAGS_SHOW_COLLIDE;
-		map_repaint(map);
 		break;
 	case KEY_UP:
 		map->player_movement &= ~(MOVING_UP);
@@ -208,7 +208,7 @@
 }
 
 static int
-cmp_tile(const struct map_tiledef *td1, const struct map_tiledef *td2)
+cmp_tile(const struct tileset_tiledef *td1, const struct tileset_tiledef *td2)
 {
 	if (td1->id < td2->id)
 		return -1;
@@ -218,19 +218,20 @@
 	return 0;
 }
 
-static struct map_tiledef *
+static struct tileset_tiledef *
 find_tiledef_by_id(const struct map *map, unsigned short id)
 {
 	typedef int (*cmp)(const void *, const void *);
 
-	const struct map_tiledef key = {
+	const struct tileset_tiledef key = {
 		.id = id
 	};
 
-	return bsearch(&key, map->tiledefs, map->tiledefsz, sizeof (key), (cmp)cmp_tile);
+	return bsearch(&key, map->tileset->tiledefs, map->tileset->tiledefsz,
+	    sizeof (key), (cmp)cmp_tile);
 }
 
-static struct map_tiledef *
+static struct tileset_tiledef *
 find_tiledef_by_row_column_in_layer(const struct map *map,
                                     const struct map_layer *layer,
                                     int row,
@@ -238,20 +239,20 @@
 {
 	unsigned short id;
 
-	if (row < 0 || (unsigned int)row >= map->h ||
-	    col < 0 || (unsigned int)col >= map->w)
+	if (row < 0 || (unsigned int)row >= map->rows ||
+	    col < 0 || (unsigned int)col >= map->columns)
 		return false;
 
-	if ((id = layer->tiles[col + row * map->w]) == 0)
+	if ((id = layer->tiles[col + row * map->columns]) == 0)
 		return NULL;
 
 	return find_tiledef_by_id(map, id - 1);
 }
 
-static struct map_tiledef *
+static struct tileset_tiledef *
 find_tiledef_by_row_column(const struct map *map, int row, int col)
 {
-	struct map_tiledef *tile;
+	struct tileset_tiledef *tile;
 
 	/* TODO: probably a for loop when we have indefinite layers. */
 	if (!(tile = find_tiledef_by_row_column_in_layer(map, &map->layers[1], row, col)))
@@ -275,15 +276,15 @@
 
 	for (int r = rowstart; r <= rowend; ++r) {
 		for (int c = colstart; c <= colend; ++c) {
-			struct map_tiledef *td;
+			struct tileset_tiledef *td;
 			struct collision tmp;
 
 			if (!(td = find_tiledef_by_row_column(map, r, c)))
 				continue;
 
 			/* Convert to absolute values. */
-			tmp.x = td->x + c * map->tileset->cellw;
-			tmp.y = td->y + r * map->tileset->cellh;
+			tmp.x = td->x + c * map->tileset->sprite->cellw;
+			tmp.y = td->y + r * map->tileset->sprite->cellh;
 			tmp.w = td->w;
 			tmp.h = td->h;
 
@@ -309,10 +310,10 @@
 {
 	assert((drow && !dcolumn) || (dcolumn && !drow));
 
-	const int playercol = map->player_x / map->tileset->cellw;
-	const int playerrow = map->player_y / map->tileset->cellh;
-	const int ncols = map->player_sprite->cellw / map->tileset->cellw;
-	const int nrows = map->player_sprite->cellh / map->tileset->cellh;
+	const int playercol = map->player_x / map->tileset->sprite->cellw;
+	const int playerrow = map->player_y / map->tileset->sprite->cellh;
+	const int ncols = map->player_sprite->cellw / map->tileset->sprite->cellw;
+	const int nrows = map->player_sprite->cellh / map->tileset->sprite->cellh;
 	int rowstart, rowend, colstart, colend;
 
 	if (drow) {
@@ -324,14 +325,14 @@
 			rowstart = 0;
 			rowend = playerrow;
 			block->x = block->y = block->h = 0;
-			block->w = map->real_w;
+			block->w = WIDTH(map);
 		} else {
 			/* Moving DOWN. */
 			rowstart = playerrow + nrows;
-			rowend = map->h;
+			rowend = HEIGHT(map);
 			block->x = block->h = 0;
-			block->y = map->real_h;
-			block->w = map->real_w;
+			block->y = HEIGHT(map);
+			block->w = WIDTH(map);
 		}
 	} else {
 		rowstart = playerrow;
@@ -342,12 +343,12 @@
 			colstart = 0;
 			colend = playercol;
 			block->x = block->y = block->w = 0;
-			block->h = map->real_h;
+			block->h = HEIGHT(map);
 		} else {
 			/* Moving RIGHT. */
 			colstart = playercol + ncols;
-			colend = map->w;
-			block->x = map->real_w;
+			colend = WIDTH(map);
+			block->x = WIDTH(map);
 			block->y = block->w = 0;
 			block->h = block->h;
 		}
@@ -376,8 +377,8 @@
 
 	if (map->view_x < 0)
 		map->view_x = 0;
-	else if (map->view_x >= (int)(map->real_w - map->view_w))
-		map->view_x = map->real_w - map->view_w;
+	else if (map->view_x >= (int)(WIDTH(map) - map->view_w))
+		map->view_x = WIDTH(map) - map->view_w;
 }
 
 static void
@@ -400,8 +401,8 @@
 
 	if (map->view_y < 0)
 		map->view_y = 0;
-	else if (map->view_y >= (int)(map->real_h - map->view_h))
-		map->view_y = map->real_h - map->view_h;
+	else if (map->view_y >= (int)(HEIGHT(map) - map->view_h))
+		map->view_y = HEIGHT(map) - map->view_h;
 }
 
 static void
@@ -438,14 +439,71 @@
 	walksprite_update(&map->player_ws, ticks);
 }
 
+static inline void
+draw_layer_tile(const struct map *map,
+                const struct map_layer *layer,
+                struct texture *colbox,
+                int start_col,
+                int start_row,
+                int start_x,
+                int start_y,
+                unsigned int r,
+                unsigned int c)
+{
+	const struct tileset_tiledef *td;
+	int index, id, sc, sr, mx, my;
+
+	index = (start_col + c) + ((start_row + r) * map->columns);
+
+	if ((id = layer->tiles[index]) == 0)
+		return;
+
+	id -= 1;
+
+	/* Sprite row/column. */
+	sc = (id) % map->tileset->sprite->ncols;
+	sr = (id) / map->tileset->sprite->ncols;
+
+	/* On screen coordinates. */
+	mx = start_x + (int)c * (int)map->tileset->sprite->cellw;
+	my = start_y + (int)r * (int)map->tileset->sprite->cellh;
+
+	tileset_draw(map->tileset, sr, sc, mx, my);
+
+	if ((td = find_tiledef_by_id(map, id)) && texture_ok(colbox))
+		texture_scale(colbox, 0, 0, 5, 5, mx + td->x, my + td->y, td->w, td->h, 0);
+
+	if (map->flags & MAP_FLAGS_SHOW_GRID) {
+		painter_set_color(0x202e37ff);
+		painter_draw_line(mx, my, mx + (int)map->tileset->sprite->cellw, my);
+		painter_draw_line(
+		    mx + (int)map->tileset->sprite->cellw - 1, my,
+		    mx + (int)map->tileset->sprite->cellw - 1, my + (int)map->tileset->sprite->cellh);
+	}
+}
+
 static void
-draw_layer(struct map *map, const struct map_layer *layer)
+draw_layer(const struct map *map, const struct map_layer *layer)
 {
 	assert(map);
 	assert(layer);
 
+	/* Beginning of view in row/column. */
+	const unsigned int start_col = map->view_x / map->tileset->sprite->cellw;
+	const unsigned int start_row = map->view_y / map->tileset->sprite->cellh;
+
+	/* Convert into x/y coordinate. */
+	const int start_x = 0 - (map->view_x % (int)map->tileset->sprite->cellw);
+	const int start_y = 0 - (map->view_y % (int)map->tileset->sprite->cellh);
+
+	/* Number of row/columns to draw starting from there. */
+	const unsigned int ncols = (map->view_w / map->tileset->sprite->cellw) + 2;
+	const unsigned int nrows = (map->view_h / map->tileset->sprite->cellh) + 2;
+
 	struct texture colbox = {0};
-	const size_t ntiles = map->w * map->h;
+
+	if (!layer->tiles)
+		return;
 
 	/* Show collision box if requested. */
 	if (map->flags & MAP_FLAGS_SHOW_COLLIDE && texture_new(&colbox, 16, 16)) {
@@ -457,36 +515,13 @@
 		PAINTER_END();
 	}
 
-	for (size_t i = 0; i < ntiles; ++i) {
-		const struct map_tiledef *td;
-		int mx, my, mr, mc, sr, sc, id;
-
-		if (layer->tiles[i] == 0)
-			continue;
-
-		id = layer->tiles[i] - 1;
-
-		/* Map row/column. */
-		mc = i % map->w;
-		mr = i / map->w;
+	for (unsigned int r = 0; r < nrows; ++r) {
+		for (unsigned int c = 0; c < ncols; ++c) {
+			if (start_col + c >= map->columns ||
+			    start_row + r >= map->rows)
+				continue;
 
-		/* Map row/column real positions. */
-		mx = mc * map->tileset->cellw;
-		my = mr * map->tileset->cellh;
-
-		/* Sprite row/column. */
-		sc = (id) % map->tileset->ncols;
-		sr = (id) / map->tileset->ncols;
-
-		sprite_draw(map->tileset, sr, sc, mx, my);
-
-		if ((td = find_tiledef_by_id(map, id)) && texture_ok(&colbox))
-			texture_scale(&colbox, 0, 0, 5, 5, mx + td->x, my + td->y, td->w, td->h, 0);
-
-		if (map->flags & MAP_FLAGS_SHOW_GRID) {
-			painter_set_color(0x202e37ff);
-			painter_draw_line(mx, my, mx + map->tileset->cellw, my);
-			painter_draw_line(mx + map->tileset->cellw - 1, my, mx + map->tileset->cellw - 1, my + map->tileset->cellh);
+			draw_layer_tile(map, layer, &colbox, start_col, start_row, start_x, start_y, r, c);
 		}
 	}
 
@@ -498,9 +533,6 @@
 {
 	assert(map);
 
-	if (!texture_new(&map->picture, map->real_w, map->real_h))
-		return false;
-
 	init(map);
 
 	return true;
@@ -542,8 +574,8 @@
 	struct texture box = {0};
 
 	/* Draw the texture about background/foreground. */
-	texture_scale(&map->picture, map->view_x, map->view_y, window.w, window.h,
-	    0, 0, window.w, window.h, 0.0);
+	draw_layer(map, &map->layers[MAP_LAYER_TYPE_BACKGROUND]);
+	draw_layer(map, &map->layers[MAP_LAYER_TYPE_FOREGROUND]);
 
 	walksprite_draw(
 		&map->player_ws,
@@ -551,6 +583,8 @@
 		map->player_x - map->view_x,
 		map->player_y - map->view_y);
 
+	draw_layer(map, &map->layers[MAP_LAYER_TYPE_ABOVE]);
+
 	action_stack_draw(&map->actions);
 
 	/* Draw collide box around player if requested. */
@@ -568,23 +602,11 @@
 }
 
 void
-map_repaint(struct map *map)
-{
-	assert(map);
-
-	PAINTER_BEGIN(&map->picture);
-	draw_layer(map, &map->layers[MAP_LAYER_TYPE_BACKGROUND]);
-	draw_layer(map, &map->layers[MAP_LAYER_TYPE_FOREGROUND]);
-	PAINTER_END();
-}
-
-void
 map_finish(struct map *map)
 {
 	assert(map);
 
 	action_stack_finish(&map->actions);
-	texture_finish(&map->picture);
 
 	memset(map, 0, sizeof (*map));
 }