summary refs log tree commit diff
diff options
context:
space:
mode:
authorzlago2024-10-04 21:38:14 +0200
committerzlago2024-10-04 21:39:03 +0200
commit99ab9bd22be2506c23d1e379f35583ddab4eadf6 (patch)
tree9020eaaf55ec6ce38a50f14e5815cbf8a572d7c0
parent54ff18bd32c932b47aa77497bc8a6fb6344894b6 (diff)
map transitions
-rw-r--r--src/entity.h11
-rw-r--r--src/funsinit.c3
-rw-r--r--src/loader.c2
-rw-r--r--src/loader.h7
-rw-r--r--src/main.c290
-rw-r--r--src/main.h19
-rw-r--r--src/player.c12
-rw-r--r--src/warp.c62
8 files changed, 313 insertions, 93 deletions
diff --git a/src/entity.h b/src/entity.h
index 354fbc1..fe76feb 100644
--- a/src/entity.h
+++ b/src/entity.h
@@ -9,6 +9,7 @@ struct entity {
 	int (*update)(struct entity *self);
 	int (*hurt)(struct entity *self, int damage);
 	int (*draw)(struct entity *self, int camx, int camy);
+	void (*free)(struct entity *self);
 	int x, y; // unsigned results in a bunch of weird edge cases
 	struct velocity {
 		signed x, y;
@@ -29,3 +30,13 @@ struct entity {
 	void *texture;
 	void *ext;
 };
+
+struct warp {
+	int (*update)(struct warp *);
+	unsigned x, y;
+	unsigned w, h;
+	unsigned tox, toy;
+	struct hitbox hitbox;
+	unsigned timer;
+	void *ext;
+};
diff --git a/src/funsinit.c b/src/funsinit.c
index 5d15cc4..25ba630 100644
--- a/src/funsinit.c
+++ b/src/funsinit.c
@@ -2,6 +2,9 @@
 #include "loader.h"
 #include "main.h"
 
+void *warp_new(struct entities *entities);
+int warp_property(void *const restrict entity, char const *const restrict property, char const *const restrict value);
 
 void funs_init(void) {
+	res_push_fun(warp_new, warp_property, "warp");
 }
diff --git a/src/loader.c b/src/loader.c
index f132b7a..2382246 100644
--- a/src/loader.c
+++ b/src/loader.c
@@ -226,7 +226,7 @@ void res_push_collision(struct blob *blob, name_T name) {
 	collisions = res_push_blob(blob, name, collisions, blob_free_func);
 }
 
-void res_push_fun(struct entity *(*newfun)(void), int (*setfun)(struct entity *const restrict self, char const *const restrict key, char const *const restrict value), name_T name) {
+void res_push_fun(void *(*newfun)(struct entities *entities), int (*setfun)(void *const restrict self, char const *const restrict key, char const *const restrict value), name_T name) {
 	int found;
 	struct fun_item new = {.funs = {.newfun = newfun, .setfun = setfun}, .name = name};
 	struct fun_item *dst = bsearchinsertposition(&new, funs.items, funs.count, sizeof (struct fun_item), res_compare_funs, &found);
diff --git a/src/loader.h b/src/loader.h
index 61f9e3c..b16fd28 100644
--- a/src/loader.h
+++ b/src/loader.h
@@ -2,6 +2,7 @@
 
 #include <stddef.h>
 #include "entity.h"
+#include "main.h"
 
 typedef char * name_T;
 
@@ -11,8 +12,8 @@ struct blob {
 };
 
 struct funs {
-	struct entity *(*newfun)(void);
-	int (*setfun)(struct entity *const restrict self, char const *const restrict key, char const *const restrict value);
+	void *(*newfun)(struct entities *entities);
+	int (*setfun)(void *const restrict self, char const *const restrict key, char const *const restrict value);
 };
 
 void res_init(void);
@@ -30,7 +31,7 @@ void res_free_collision(void);
 struct blob res_get_collision(name_T const name);
 
 void res_free_fun(void);
-void res_push_fun(struct entity *(*newfun)(void), int (*setfun)(struct entity *const restrict self, char const *const restrict key, char const *const restrict value), name_T name);
+void res_push_fun(void *(*newfun)(struct entities *entities), int (*setfun)(void *const restrict self, char const *const restrict key, char const *const restrict value), name_T name);
 struct funs res_get_fun(name_T const name);
 
 int loadResources(char *filename);
diff --git a/src/main.c b/src/main.c
index 2d3e89d..c48deef 100644
--- a/src/main.c
+++ b/src/main.c
@@ -19,6 +19,8 @@
 
 SDL_Window *window = NULL;
 SDL_Renderer *renderer = NULL;
+enum game_state game_state = STATE_PLAYING;
+char *game_next_level;
 
 #define WINDOW_WIDTH 160
 #define WINDOW_HEIGHT 90
@@ -34,13 +36,134 @@ SDL_Scancode keybinds[] = {
 	SDL_SCANCODE_S,
 };
 
-struct entity player[1] = {0};
+struct entities entities = {{{0}}, {{0}}, 0, {{0}}, 0}, next_entities = {{{0}}, {{0}}, 0, {{0}}, 0};
 
-struct entity *player_new(void);
-int player_property(struct entity *const restrict entity, char const *const restrict property, char const *const restrict value);
+void *player_new(struct entities *entities);
+int player_property(void *const restrict entity, char const *const restrict property, char const *const restrict value);
 
 void funs_init(void);
 
+void entities_free(struct entities *entities) {
+	for (int i = 0, e = 0; i < 64 && e < entities->enemies; i++) {
+		if (entities->enemy[i].state) {
+			e++;
+			if (entities->enemy[i].free != NULL) {
+				entities->enemy[i].free(entities->enemy + i);
+			}
+		}
+	}
+	entities->enemies = 0;
+	#if 0
+	for (int i = 0; i < entities.warps; i++) {
+		entities.warp[i].update(entities.warp + i);
+	}
+	#endif
+	entities->warps = 0;
+}
+
+int entities_load(struct entities *entities, char *data, size_t size, size_t input_bytes) {
+	entities->enemies = 0;
+	entities->warps = 0;
+	data += input_bytes;
+	size -= input_bytes;
+	while (size) {
+		size_t len;
+		if ((len = strnlen(data, size)) == size) {
+			return 1;
+		}
+		char *name = data;
+		struct funs fun = res_get_fun(name);
+		data += len + 1;
+		size -= len + 1;
+		if (fun.newfun) {
+			struct entity *entity = fun.newfun(&next_entities);
+			while (1) {
+				if ((len = strnlen(data, size)) == size) {
+					return 1;
+				} else if (len == 0) {
+					data++;
+					size--;
+					break;
+				}
+				char *key = data;
+				data += len + 1;
+				size -= len + 1;
+				if ((len = strnlen(data, size)) == size) {
+					return 1;
+				}
+				char *value = data;
+				data += len + 1;
+				size -= len + 1;
+				if (fun.setfun(entity, key, value)) {
+					puts(key); puts(value);
+					return 1;
+				}
+			}
+		} else {
+			fprintf(stderr, "warn: unknown entity %s\n", name);
+			while (1) {
+				if ((len = strnlen(data, size)) == size) {
+					return 1;
+				} else if (len == 0) {
+					data++;
+					size--;
+					break;
+				}
+				data += len + 1;
+				size -= len + 1;
+				if ((len = strnlen(data, size)) == size) {
+					return 1;
+				}
+				data += len + 1;
+				size -= len + 1;
+			}
+		}
+	}
+	return 0;
+}
+
+int game_update(void) {
+	if (entities.player[0].update(entities.player)) {
+		return 1;
+	}
+	
+	for (int i = 0, e = 0; i < 64 && e < entities.enemies; i++) {
+		if (entities.enemy[i].state) {
+			e++;
+			entities.enemy[i].update(entities.enemy + i);
+		}
+	}
+	
+	for (int i = 0; i < entities.warps; i++) {
+		entities.warp[i].update(entities.warp + i);
+	}
+	
+	return 0;
+}
+
+void game_render(SDL_Texture *framebuffer, int x, int y) {
+	SDL_SetRenderTarget(renderer, framebuffer);
+	tilemap_background(tilemap, x, y, WINDOW_WIDTH, WINDOW_HEIGHT);
+	entities.player[0].draw(entities.player, x, y);
+	for (int i = 0, e = 0; i < 64 && e < entities.enemies; i++) {
+		if (entities.enemy[i].state) {
+			e++;
+			entities.enemy[i].draw(entities.enemy + i, x, y);
+		}
+	}
+	tilemap_foreground(tilemap, x, y, WINDOW_WIDTH, WINDOW_HEIGHT);
+	//SDL_RenderCopy(renderer, res_get_texture("meow").data, &(SDL_Rect) {0, 0, 128, 90}, &(SDL_Rect) {0, 0, 128, 90});
+}
+
+void game_render_flush(SDL_Texture *framebuffer) {
+	SDL_SetRenderTarget(renderer, NULL);
+	SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0);
+	SDL_RenderClear(renderer);
+	SDL_RenderCopy(renderer, framebuffer, &(SDL_Rect) {0, 0, WINDOW_WIDTH, WINDOW_HEIGHT}, &(SDL_Rect) {0, 0, WINDOW_WIDTH, WINDOW_HEIGHT});
+	// then we wait for the next video frame 
+	SDL_RenderPresent(renderer);
+}
+
 int main(int const argc, char *const *const argv) {
 	struct option opts[] = {
 		{"help",       0, NULL, 'h'},
@@ -176,60 +299,19 @@ int main(int const argc, char *const *const argv) {
 				optind++;
 			}
 		}
-		struct blob map = res_get_map("untitled");
-		tilemap = tilemap_load(map.data, map.size);
-		char *a = (char *) map.data + tilemap->input_bytes;
-		map.size -= tilemap->input_bytes;
-		while (map.size) {
-			size_t len;
-			if ((len = strnlen(a, map.size)) == map.size) {
-				return 1; // hack
-			}
-			char *name = a;
-			puts(name);
-			struct funs fun = res_get_fun(name);
-			a += len + 1;
-			map.size -= len + 1;
-			if (fun.newfun) {
-				struct entity *entity = fun.newfun();
-				while (1) {
-					if ((len = strnlen(a, map.size)) == map.size) {
-						return 1; // hack
-					} else if (len == 0) {
-						a++;
-						map.size--;
-						break;
-					}
-					char *key = a;
-					a += len + 1;
-					map.size -= len + 1;
-					if ((len = strnlen(a, map.size)) == map.size) {
-						return 1; // hack
-					}
-					char *value = a;
-					a += len + 1;
-					map.size -= len + 1;
-					if (fun.setfun(entity, key, value)) {
-						return 1; // hack
-					}
-				}
-			} else {
-				while (1) { // hack
-					if ((len = strnlen(a, map.size)) == map.size) { // hack
-						return 1; // hack
-					} else if (len == 0) {
-						a++;
-						map.size--;
-						break;
-					}
-					a += len + 1;
-					map.size -= len + 1;
-				}
-			}
+
+		struct blob blob = res_get_map("untitled");
+		next_tilemap = tilemap_load(blob.data, blob.size);
+		if (next_tilemap == NULL) {
+			fputs("failed to load initial tilemap\n", stderr);
+			goto end;
 		}
-		//memmem(map.data + tilemap->byte_count, map.size - tilemap->byte_count, (char []) {0, 0}, 2);
-		printf("load_tilemap %p\n", (void *) tilemap);
-		SDL_SetRenderTarget(renderer, NULL);
+		if (entities_load(&entities, blob.data, blob.size, next_tilemap->input_bytes)) {
+			tilemap_free(next_tilemap);
+			fputs("failed to load initial tilemap\n", stderr);
+			goto end;
+		}
+		tilemap = next_tilemap;
 	}
 
 	/* unsigned error; // identical variable is declared higher up */
@@ -251,10 +333,11 @@ int main(int const argc, char *const *const argv) {
 
 	SDL_ShowWindow(window);
 	
-	int x = 0, y = 0;
-	player_new();
-	player_property(player, "x", "40");
-	player_property(player, "y", "64");
+	int x = 0, y = 0, fade = 0;
+	player_new(&next_entities);
+	player_property(next_entities.player, "x", "40");
+	player_property(next_entities.player, "y", "64");
+	memcpy(&entities, &next_entities, sizeof (entities));
 	while (1) {
 		SDL_Event evt;
 		while (SDL_PollEvent(&evt)) {
@@ -352,29 +435,72 @@ int main(int const argc, char *const *const argv) {
 			}
 		}
 
-		//SDL_RenderCopy(renderer, tilemap->wang_tileset, &(SDL_Rect) {0, 0, 128, 90}, &(SDL_Rect) {0, 0, 128, 90});
-		if (player[0].update(player)) {
-			break; // TODO: game over state
+		switch (game_state) {
+			case STATE_PLAYING:
+				//SDL_RenderCopy(renderer, tilemap->wang_tileset, &(SDL_Rect) {0, 0, 128, 90}, &(SDL_Rect) {0, 0, 128, 90});
+				if (game_update()) {
+					goto end; // TODO: game over state
+				}
+				
+				x = (entities.player[0].x / 16) - (WINDOW_WIDTH / 2);
+				y = (entities.player[0].y / 16) - (WINDOW_HEIGHT / 2);
+				if (x < 0) {x = 0;} else if (x + WINDOW_WIDTH > tilemap->width * 8) {x = tilemap->width * 8 - WINDOW_WIDTH;}
+				if (y < 0) {y = 0;} else if (y + WINDOW_HEIGHT > tilemap->height * 8) {y = tilemap->height * 8 - WINDOW_HEIGHT;}
+				
+				game_render(framebuffer, x, y);
+				game_render_flush(framebuffer);
+				break;
+
+			case STATE_FADE_IN:
+				fade += 15;
+				SDL_SetRenderTarget(renderer, NULL);
+				SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0);
+				SDL_RenderClear(renderer);
+				SDL_RenderCopy(renderer, framebuffer, &(SDL_Rect) {0, 0, WINDOW_WIDTH, WINDOW_HEIGHT}, &(SDL_Rect) {0, 0, WINDOW_WIDTH, WINDOW_HEIGHT});
+				SDL_SetRenderDrawColor(renderer, 0, 0, 0, fade);
+				SDL_RenderFillRect(renderer, &(SDL_Rect) {0, 0, WINDOW_WIDTH, WINDOW_HEIGHT});
+				SDL_RenderPresent(renderer);
+				if (fade == 255) {
+					struct blob blob = res_get_map(game_next_level);
+					next_tilemap = tilemap_load(blob.data, blob.size);
+					if (next_tilemap != NULL) {
+						if (entities_load(&next_entities, blob.data, blob.size, next_tilemap->input_bytes)) {
+							tilemap_free(next_tilemap);
+						} else {
+							tilemap_free(tilemap);
+							tilemap = next_tilemap;
+							entities_free(&entities);
+							memcpy(&entities, &next_entities, sizeof (entities));
+						}
+					}
+					game_state = STATE_FADE_OUT;
+
+					x = (entities.player[0].x / 16) - (WINDOW_WIDTH / 2);
+					y = (entities.player[0].y / 16) - (WINDOW_HEIGHT / 2);
+					if (x < 0) {x = 0;} else if (x + WINDOW_WIDTH > tilemap->width * 8) {x = tilemap->width * 8 - WINDOW_WIDTH;}
+					if (y < 0) {y = 0;} else if (y + WINDOW_HEIGHT > tilemap->height * 8) {y = tilemap->height * 8 - WINDOW_HEIGHT;}
+					
+					game_render(framebuffer, x, y);
+					SDL_SetRenderTarget(renderer, NULL);
+					SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0);
+					SDL_RenderClear(renderer);
+				}
+				break;
+			
+			case STATE_FADE_OUT:
+				fade -= 15;
+				SDL_SetRenderTarget(renderer, NULL);
+				SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0);
+				SDL_RenderClear(renderer);
+				SDL_RenderCopy(renderer, framebuffer, &(SDL_Rect) {0, 0, WINDOW_WIDTH, WINDOW_HEIGHT}, &(SDL_Rect) {0, 0, WINDOW_WIDTH, WINDOW_HEIGHT});
+				SDL_SetRenderDrawColor(renderer, 0, 0, 0, fade);
+				SDL_RenderFillRect(renderer, &(SDL_Rect) {0, 0, WINDOW_WIDTH, WINDOW_HEIGHT});
+				SDL_RenderPresent(renderer);
+				if (fade == 0) {
+					game_state = STATE_PLAYING;
+				}
+				break;
 		}
-		
-		//x += input_right(input_now) - input_left(input_now);
-		//y += input_down(input_now) - input_up(input_now);
-		x = (player[0].x / 16) - (WINDOW_WIDTH / 2);
-		y = (player[0].y / 16) - (WINDOW_HEIGHT / 2);
-		if (x < 0) {x = 0;} else if (x + WINDOW_WIDTH > tilemap->width * 8) {x = tilemap->width * 8 - WINDOW_WIDTH;}
-		if (y < 0) {y = 0;} else if (y + WINDOW_HEIGHT > tilemap->height * 8) {y = tilemap->height * 8 - WINDOW_HEIGHT;}
-		
-		SDL_SetRenderTarget(renderer, framebuffer);
-		tilemap_background(tilemap, x, y, WINDOW_WIDTH, WINDOW_HEIGHT);
-		player[0].draw(player, x, y);
-		tilemap_foreground(tilemap, x, y, WINDOW_WIDTH, WINDOW_HEIGHT);
-		//SDL_RenderCopy(renderer, res_get_texture("meow").data, &(SDL_Rect) {0, 0, 128, 90}, &(SDL_Rect) {0, 0, 128, 90});
-		SDL_SetRenderTarget(renderer, NULL);
-		SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0);
-		SDL_RenderClear(renderer);
-		SDL_RenderCopy(renderer, framebuffer, &(SDL_Rect) {0, 0, WINDOW_WIDTH, WINDOW_HEIGHT}, &(SDL_Rect) {0, 0, WINDOW_WIDTH, WINDOW_HEIGHT});
-		// then we wait for the next video frame 
-		SDL_RenderPresent(renderer);
 	}
 	
 	end:
diff --git a/src/main.h b/src/main.h
index ad0951f..d743ce7 100644
--- a/src/main.h
+++ b/src/main.h
@@ -7,4 +7,21 @@ extern SDL_Renderer *renderer;
 
 extern unsigned input_now;
 
-extern struct entity player[1];
+extern struct entities {
+	struct entity player[1];
+	struct entity enemy[64];
+	unsigned enemies;
+	struct warp warp[16];
+	unsigned warps;
+} entities, next_entities;
+
+extern enum game_state {
+	STATE_PLAYING,
+	STATE_FADE_IN,
+	STATE_FADE_OUT,
+} game_state;
+
+extern char *game_next_level;
+
+void entities_free(struct entities *entities);
+int entities_load(struct entities *entities, char *data, size_t size, size_t input_bytes);
diff --git a/src/player.c b/src/player.c
index b23b2f0..0f0025e 100644
--- a/src/player.c
+++ b/src/player.c
@@ -114,7 +114,7 @@ static int move(struct entity *self) {
 	return on_ground;
 }
 
-void anim(struct entity *self, unsigned anim) {
+static void anim(struct entity *self, unsigned anim) {
 	self->anim = player_anims[anim];
 }
 
@@ -210,8 +210,8 @@ static int player_draw(struct entity *self, int camX, int camY) {
 	return 0;
 }
 
-struct entity *player_new(void) {
-	player[0] = (struct entity) {
+struct entity *player_new(struct entities *entities) {
+	entities->player[0] = (struct entity) {
 		.update = player_update,
 		.hurt = player_hurt,
 		.draw = player_draw,
@@ -228,9 +228,9 @@ struct entity *player_new(void) {
 		.texture = NULL,
 		.ext = NULL,
 	};
-	anim(player, PLAYER_A_IDLE);
-	player[0].texture = res_get_texture("fywi").data;
-	return player + 0;
+	anim(entities->player, PLAYER_A_IDLE);
+	entities->player[0].texture = res_get_texture("fywi").data;
+	return entities->player + 0;
 }
 
 int player_property(struct entity *const restrict self, char const *const restrict property, char const *const restrict value) {
diff --git a/src/warp.c b/src/warp.c
new file mode 100644
index 0000000..f36b716
--- /dev/null
+++ b/src/warp.c
@@ -0,0 +1,62 @@
+#include "main.h"
+#include "entity.h"
+#include "loader.h"
+#include "tilemap.h"
+
+static int warp_update(struct warp *self) {
+	if (
+		self->hitbox.left < entities.player[0].hitbox.right &&
+		self->hitbox.right > entities.player[0].hitbox.left &&
+		self->hitbox.top < entities.player[0].hitbox.bottom &&
+		self->hitbox.bottom > entities.player[0].hitbox.top
+	) {
+		if (self->ext == NULL) {
+			return 0;
+		}
+		game_next_level = self->ext;
+		memcpy(next_entities.player, entities.player, sizeof (entities.player));
+		next_entities.player[0].x = self->tox * 16;
+		next_entities.player[0].y = self->toy * 16;
+		game_state = STATE_FADE_IN;
+		self->ext = NULL;
+		return 0;
+	}
+	return 0;
+}
+
+struct warp *warp_new(struct entities *entities) {
+	entities->warp[entities->warps] = (struct warp) {
+		.update = warp_update,
+		.x = 0, .y = 0,
+		.w = 0, .h = 0,
+		.tox = 0, .toy = 0,
+		.hitbox = {
+			0, 0, 0, 0,
+		},
+		.timer = 0,
+		.ext = NULL,
+	};
+	return entities->warp + entities->warps++;
+}
+
+int warp_property(struct warp *const restrict self, char const *const restrict property, char const *const restrict value) {
+	if (strcmp(property, "x") == 0) {
+		self->x = atoi(value);
+	} else if (strcmp(property, "y") == 0) {
+		self->y = atoi(value);
+	} else if (strcmp(property, "width") == 0) {
+		self->w = atoi(value);
+	} else if (strcmp(property, "height") == 0) {
+		self->h = atoi(value);
+	} else if (strcmp(property, "tox") == 0) {
+		self->tox = atoi(value);
+	} else if (strcmp(property, "toy") == 0) {
+		self->toy = atoi(value);
+	} else if (strcmp(property, "map") == 0) {
+		self->ext = value;
+	} else {
+		return 1;
+	}
+	self->hitbox = (struct hitbox) {.left = self->x, .top = self->y, .right = self->x + self->w, .bottom = self->y + self->h};
+	return 0;
+}