diff librpg/rpg/map.c @ 148:c577c15df07f

misc: split libraries, closes #2496
author David Demelier <markand@malikania.fr>
date Thu, 15 Oct 2020 10:32:18 +0200
parents libcore/core/map.c@c679e08b32b2
children aab824406d3d
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/librpg/rpg/map.c	Thu Oct 15 10:32:18 2020 +0200
@@ -0,0 +1,237 @@
+/*
+ * map.c -- game map
+ *
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <core/error.h>
+#include <core/error_p.h>
+#include <core/image.h>
+#include <core/painter.h>
+#include <core/sprite.h>
+#include <core/sys.h>
+#include <core/texture.h>
+#include <core/window.h>
+
+#include "map.h"
+
+/* Create %<v>c string literal for scanf */
+#define MAX_F(v) MAX_F_(v)
+#define MAX_F_(v) "%" #v "c"
+
+static void
+parse_layer(struct map_data *data, const char *line, FILE *fp)
+{
+	char layer_name[32 + 1] = { 0 };
+	struct map_layer *layer;
+	size_t amount, current;
+
+	/* Determine layer. */
+	if (sscanf(line, "layer|%32s", layer_name) <= 0)
+		return;
+	if (strcmp(layer_name, "background") == 0)
+		layer = &data->layers[0];
+	else if (strcmp(layer_name, "foreground") == 0)
+		layer = &data->layers[1];
+	else
+		return;
+
+	/* Check if weight/height has been specified. */
+	if (data->w == 0 || data->h == 0)
+		return;
+
+	amount = data->w * data->h;
+	current = 0;
+
+	if (!(layer->tiles = calloc(amount, sizeof (unsigned short))))
+		return;
+
+	for (int tile; fscanf(fp, "%d", &tile) && current < amount; ++current)
+		layer->tiles[current] = tile;
+}
+
+static void
+parse(struct map_data *data, const char *line, FILE *fp)
+{
+	if (strncmp(line, "title", 5) == 0)
+		sscanf(line, "title|" MAX_F(MAP_TITLE_MAX), data->title);
+	else if (strncmp(line, "width", 5) == 0)
+		sscanf(line, "width|%u", &data->w);
+	else if (strncmp(line, "height", 6) == 0)
+		sscanf(line, "height|%u", &data->h);
+	else if (strncmp(line, "tilewidth", 9) == 0)
+		sscanf(line, "tilewidth|%hu", &data->tile_w);
+	else if (strncmp(line, "tileheight", 10) == 0)
+		sscanf(line, "tileheight|%hu", &data->tile_h);
+	else if (strncmp(line, "origin", 6) == 0)
+		sscanf(line, "origin|%d|%d", &data->origin_x, &data->origin_y);
+	else if (strncmp(line, "tileset", 7) == 0)
+		sscanf(line, "tileset|" MAX_F(MAP_TILESET_MAX), data->tileset);
+	else if (strncmp(line, "layer", 5) == 0)
+		parse_layer(data, line, fp);
+}
+
+static bool
+check(struct map_data *data)
+{
+	if (strlen(data->title) == 0)
+		return error_printf("data has no title");
+	if (data->w == 0 || data->h == 0)
+		return error_printf("data has null sizes");
+	if (data->tile_w == 0 || data->tile_h == 0)
+		return error_printf("data has null tile sizes");
+	if (!data->layers[0].tiles || !data->layers[1].tiles)
+		return error_printf("could not allocate data");
+
+	return true;
+}
+
+static void
+draw_layer(struct map *map, const struct map_layer *layer)
+{
+	assert(map);
+	assert(layer);
+
+	struct sprite sprite;
+	int x = 0, y = 0;
+
+	sprite_init(&sprite, map->tileset, map->data->tile_w, map->data->tile_h);
+
+	for (unsigned int r = 0; r < map->data->w; ++r) {
+		for (unsigned int c = 0; c < map->data->h; ++c) {
+			unsigned int si = r * map->data->w + c;
+			unsigned int sr = (layer->tiles[si] - 1) / sprite.ncols;
+			unsigned int sc = (layer->tiles[si] - 1) % sprite.nrows;
+
+			if (layer->tiles[si] != 0)
+				sprite_draw(&sprite, sr, sc, x, y);
+
+			x += map->data->tile_w;
+		}
+
+		x = 0;
+		y += map->data->tile_h;
+	}
+}
+
+bool
+map_data_open_fp(struct map_data *data, FILE *fp)
+{
+	assert(data);
+
+	char line[1024];
+
+	if (!fp)
+		return false;
+
+	memset(data, 0, sizeof (*data));
+
+	while (fgets(line, sizeof (line), fp)) {
+		/* Remove \n if any */
+		line[strcspn(line, "\n")] = '\0';
+		parse(data, line, fp);
+	}
+
+	fclose(fp);
+
+	if (!check(data)) {
+		map_data_finish(data);
+		return false;
+	}
+
+	/* Compute real size. */
+	data->real_w = data->w * data->tile_w;
+	data->real_h = data->h * data->tile_h;
+
+	return true;
+}
+
+bool
+map_data_open(struct map_data *data, const char *path)
+{
+	assert(data);
+	assert(path);
+
+	return map_data_open_fp(data, fopen(path, "r"));
+}
+
+bool
+map_data_openmem(struct map_data *data, const void *buf, size_t bufsz)
+{
+	assert(data);
+	assert(buf);
+
+	return map_data_open_fp(data, fmemopen((void *)buf, bufsz, "r"));
+}
+
+void
+map_data_finish(struct map_data *data)
+{
+	assert(data);
+
+	free(data->layers[0].tiles);
+	free(data->layers[1].tiles);
+
+	memset(data, 0, sizeof (*data));
+}
+
+bool
+map_init(struct map *map, struct map_data *data, struct texture *tileset)
+{
+	assert(map);
+	assert(data);
+	assert(tileset && texture_ok(tileset));
+
+	if (!(texture_new(&map->picture, data->real_w, data->real_h)))
+		return false;
+
+	map->data = data;
+	map->tileset = tileset;
+
+	map_repaint(map);
+
+	return true;
+}
+
+void
+map_draw(struct map *map, int srcx, int srcy)
+{
+	texture_scale(&map->picture, srcx, srcy, window.w, window.h,
+	    0, 0, window.w, window.h, 0.0);
+}
+
+void
+map_repaint(struct map *map)
+{
+	PAINTER_BEGIN(&map->picture);
+	draw_layer(map, &map->data->layers[0]);
+	draw_layer(map, &map->data->layers[1]);
+	PAINTER_END();
+}
+
+void
+map_finish(struct map *map)
+{
+	assert(map);
+
+	texture_finish(&map->picture);
+
+	memset(map, 0, sizeof (*map));
+}