summaryrefslogtreecommitdiff
path: root/src/world.c
blob: be6d5d0dc38dd96be67ede82b53df3f68612c2e3 (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
129
130
131
132
133
134
135
136
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] - 1;
	return -1; // oob tile
}

int2
world_find_cell(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);
}

int2
world_find(int tile)
{
	with (cell, g_world.cells[g_world.x + g_world.y * g_world.width])
		return cell_find(cell, tile);
	return I2(-1, -1);
}