summaryrefslogtreecommitdiff
path: root/src/world.c
blob: a24c7a6d8e4aeea8a5089c625e9985dc3bcbba6f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
World g_world = {0};

#define expect(X) if (!(X)) { \
	perr("expect failed: "STR(X)); \
	if (fp != NULL) fclose(fp); \
	world_deinit(); \
	exit(EXIT_FAILURE); \
}

void
world_init(const char *path, const char *cell_pattern)
{
	assert(path != NULL);
	assert(cell_pattern != NULL);

	FILE *fp = fopen(path, "rw");
	expect(fp != NULL);

	int cell_count = 0;
	fscanf(fp, "%d[^,\n", &cell_count);
	fscanf(fp, "%*c");
	expect(cell_count > 0);

	// skip x and y
	fscanf(fp, "%*d[^,\n]");
	fscanf(fp, "%*c");
	fscanf(fp, "%*d[^,\n]");
	fscanf(fp, "%*c");

	int min_x = INT_MAX, min_y = INT_MAX, max_x = INT_MIN, max_y = INT_MIN;
	rfor (i, 0, cell_count) {
		int id = 0, x = 0, y = 0;
		fscanf(fp, "%d[^,\n]", &id);
		fscanf(fp, "%*c");
		fscanf(fp, "%d[^,\n]", &x);
		fscanf(fp, "%*c");
		fscanf(fp, "%d[^,\n]", &y);
		fscanf(fp, "%*c");

		min_x = min(x, min_x);
		min_y = min(x, min_y);
		max_x = max(x, max_x);
		max_y = max(x, max_y);

		Cell *const cell = cell_load(cell_pattern, id, x, y);
		expect(cell != NULL);
		cell->next = g_world.root_cell;
		g_world.root_cell = cell;
	}
	expect(g_world.root_cell != NULL);

	fclose(fp);
	fp = NULL;

	// flatten the cell structure
	g_world.width = max_x - min_x + 1;
	g_world.height = max_y - min_y + 1;
	g_world.cells = alloc(sizeof(Cell*) * g_world.width * g_world.height);
	expect(g_world.cells != NULL);

	foreach (cell, g_world.root_cell) {
		const int cell_x = cell->x - min_x;
		const int cell_y = cell->y - min_y;
		g_world.cells[cell_x + cell_y * g_world.width] = cell;
	}
}

void
world_deinit(void)
{
	if (g_world.cells != NULL) {
		free(g_world.cells);
		g_world.cells = NULL;
	}
	cell_destroy(g_world.root_cell, true);
}

void
world_draw(void)
{
	with (cell, g_world.cells[g_world.x + g_world.y * g_world.width])
		cell_draw(cell, 0, 0);
}

int
world_get(int x, int y)
{
	int wx = g_world.x;
	int wy = g_world.y;

	if (x < 0) {
		x += cfg.tile_width * cfg.cell_width;
		wx -= 1;
	} else if (x >= cfg.tile_width * cfg.cell_width) {
		x -= cfg.tile_width * cfg.cell_width;
		wx += 1;
	}
	if (y < 0) {
		y += cfg.tile_height * cfg.cell_height;
		wy -= 1;
	} else if (y >= cfg.tile_height * cfg.cell_height) {
		y -= cfg.tile_height * cfg.cell_height;
		wy += 1;
	}

	const int i = x / cfg.tile_width + (y / cfg.tile_height) * cfg.cell_width;
	with (cell, g_world.cells[wx + wy * g_world.width])
		return cell->data[i] ? (cell->data[i] - 1) : 0;
	return 0; // oob tile
}

int2
world_find(int tile)
{
	rfor (y, 0, g_world.height) {
		rfor (x, 0, g_world.width) {
			Cell *const cell = g_world.cells[x + y * g_world.width];
			if (cell == NULL)
				continue;
			const int2 spawn = cell_find(cell, tile + 1);
			if (spawn.x < 0)
				continue;
			return I2(x, y);
		}
	}

	return I2(-1, -1);
}