From 5f341eacdf0d75a4b334969a2d8a4701d61e4d9e Mon Sep 17 00:00:00 2001
From: quou <quou@disroot.org>
Date: Sun, 7 May 2023 12:53:46 +1000
Subject: Add waves and stuff.

---
 Makefile           |   1 +
 animation.c        |  15 ++++++
 animation.h        |   3 +-
 collision_system.c |  38 ++++++++++++++-
 components.h       |   3 +-
 enemy.c            |   3 +-
 game.c             | 134 ++++++++++++++++++++++++++++++++++++++++++++---------
 game.h             |   4 ++
 game_config.h      |   4 +-
 menu.c             |   4 +-
 platform.c         |   4 ++
 platform.h         |   2 +
 player.c           |  11 +++--
 player.h           |   2 +-
 spawner.c          |  68 +++++++++++++++++++++++++++
 spawner.h          |  12 +++++
 systems.h          |   3 +-
 wave.c             |  45 ++++++++++++++++--
 wave.h             |   4 ++
 world.h            |   3 +-
 20 files changed, 325 insertions(+), 38 deletions(-)
 create mode 100644 spawner.c
 create mode 100644 spawner.h

diff --git a/Makefile b/Makefile
index 715391a..2bc251d 100644
--- a/Makefile
+++ b/Makefile
@@ -103,6 +103,7 @@ sources = \
   rect.c \
   render.c \
   solid.c \
+  spawner.c \
   sprite.c \
   sprite_system.c \
   standard.c \
diff --git a/animation.c b/animation.c
index ff944eb..1912934 100644
--- a/animation.c
+++ b/animation.c
@@ -79,6 +79,21 @@ static const Animation animations[] = {
 		},
 		6,
 		3
+	},
+	/* animation_spawn */
+	{
+		{
+			{ 133, 60, 15, 6 },
+			{ 133, 60, 15, 6 },
+			{ 133, 60, 15, 6 },
+			{ 133, 60, 15, 6 },
+			{ 133, 66, 15, 6 },
+			{ 148, 60, 15, 6 },
+			{ 148, 66, 15, 6 },
+			{ 148, 72, 15, 6 },
+		},
+		8,
+		5
 	}
 };
 
diff --git a/animation.h b/animation.h
index 0a36043..27eadb8 100644
--- a/animation.h
+++ b/animation.h
@@ -11,7 +11,8 @@ typedef enum {
 	animation_player_idle_right,
 	animation_enemy_bullet_explode,
 	animation_player_bullet_explode,
-	animation_heart_break
+	animation_heart_break,
+	animation_spawn
 } Animation_ID;
 
 typedef struct {
diff --git a/collision_system.c b/collision_system.c
index 2bb8cdb..a5342d2 100644
--- a/collision_system.c
+++ b/collision_system.c
@@ -175,12 +175,13 @@ static void handle(
 }
 
 void collision_system(World* world) {
-	int i, j, k, s, s2, p0x, p0y, r, l, t, b;
+	int i, j, k, s, s2, p0x, p0y, r, l, t, b, dx, dy, d;
 	int ax, ay, aw, ah, bx, by, bw, bh;
 	int overlap[4];
 	unsigned bits;
-	const CPosition* pos0, * pos1;
+	CPosition* pos0, * pos1;
 	const CCollider* col0, * col1;
+	const CEnemy* e0, * e1;
 	Collision_Side side;
 
 	for (i = 0; i < world->entity_count; i++) {
@@ -254,6 +255,39 @@ void collision_system(World* world) {
 			}
 		}
 	}
+
+	for (i = 0; i < world->entity_count; i++) {
+		bits = world->bitmask[i];
+		if (
+			!(bits & ctype_enemy) ||
+			!(bits & ctype_position)
+		) { continue; }
+
+		pos0 = &world->positions[i];
+		e0   = &world->enemies[i];
+
+		for (j = i + 1; j < world->entity_count; j++) {
+			bits = world->bitmask[j];
+			if (
+				!(bits & ctype_enemy) ||
+				!(bits & ctype_position)
+			) { continue; }
+
+			pos1 = &world->positions[j];
+			e1   = &world->enemies[j];
+
+			dx = (pos0->x - pos1->x) >> 4;
+			dy = (pos0->y - pos1->y) >> 4;
+			d = ((dx * dx) + (dy * dy)) >> fbits;
+
+			if (d < enemy_min_distance) {
+				pos0->x += dx;
+				pos1->x -= dx;
+				pos0->y += dy;
+				pos1->y -= dy;
+			}
+		}
+	}
 }
 
 #endif
diff --git a/components.h b/components.h
index fb0df18..b759ec9 100644
--- a/components.h
+++ b/components.h
@@ -61,7 +61,8 @@ typedef enum {
 	ctype_player               = 1 << 10,
 	ctype_destroy_on_anim_done = 1 << 11,
 	ctype_solid                = 1 << 12,
-	ctype_moveable             = 1 << 13
+	ctype_moveable             = 1 << 13,
+	ctype_spawner              = 1 << 14
 } CType;
 
 #endif
diff --git a/enemy.c b/enemy.c
index 04baec4..60366fb 100644
--- a/enemy.c
+++ b/enemy.c
@@ -105,6 +105,7 @@ void enemy_system(World* world) {
 			enemy = &world->enemies[i];
 			if (enemy->hp <= 0) {
 				destroy_entity(world, i);
+				world->enemies_killed++;
 
 				if ((bits & ctype_position) && (bits & ctype_skull)) {
 					pos = &world->positions[i];
@@ -130,7 +131,7 @@ void enemy_system(World* world) {
 			dpy = ((ppos->y - pos->y) >> 4) + 256;
 			tpx = dpx;
 			tpy = dpy;
-			d = ((dpx * dpx) >> fbits) + ((dpy * dpy) >> fbits);
+			d = ((dpx * dpx) + (dpy * dpy)) >> fbits;
 
 			if (enemy->being_damaged) {
 				enemy->being_damaged--;
diff --git a/game.c b/game.c
index b68d85a..064fc22 100644
--- a/game.c
+++ b/game.c
@@ -1,15 +1,38 @@
 #include "asset.h"
 #include "enemy.h"
 #include "game.h"
+#include "input.h"
 #include "platform.h"
 #include "sprite.h"
 #include "standard.h"
 #include "systems.h"
 
-static void menu_new(Menu* menu) {
+void gameplay_new(Game* game) {
+	World* world;
+
+	world = &game->world;
+
+	seed_rng(platform_get_time());
+	init_world(&game->world);
+	init_map(&world->map, world);
+	init_player(&world->player, world);
+	init_waver(&world->waver);
+	world->gmemory = gmemory_max;
+}
+
+void gameplay_next_wave(Game* game) {
+	World* world;
+
+	world = &game->world;
+	next_wave(&world->waver);
+	game_change_state(game, game_state_game);
+}
+
+static void menu_start(Menu* menu) {
 	Game* game;
 
 	game = menu->ptr;
+	gameplay_new(game);
 	game_change_state(game, game_state_game);
 }
 
@@ -17,6 +40,7 @@ static void menu_restart(Menu* menu) {
 	Game* game;
 
 	game = menu->ptr;
+	gameplay_new(game);
 	game_change_state(game, game_state_game);
 }
 
@@ -45,7 +69,7 @@ static void menu_init(Game* game) {
 	font = get_default_font();
 
 	init_menu(&game->menu, game);
-	menu_add(&game->menu, "Play", menu_new, font);
+	menu_add(&game->menu, "Play", menu_start, font);
 	menu_add(&game->menu, "Credits", menu_credits, font);
 	menu_add(&game->menu, "Quit", menu_quit, font);
 }
@@ -95,17 +119,11 @@ static void menu_deinit(Game* game) {
 }
 
 static void gameplay_init(Game* game) {
-	World* world;
-
-	world = &game->world;
-
-	seed_rng(500);
-	init_world(&game->world);
-	init_map(&world->map, world);
-	init_player(&world->player, world);
-	init_waver(&world->waver);
+	start_wave(&game->world);
 
-	world->gmemory = gmemory_max;
+	game->want_next = 0;
+	game->wave_timer = 0;
+	game->display_wave_text = 100;
 }
 
 static void render_hud(Game* game) {
@@ -164,27 +182,73 @@ static void render_hud(Game* game) {
 }
 
 static void gameplay_update(Game* game) {
-	int cx, cy;
+	int cx, cy, ec, l;
 	World* world;
 	Player* player;
-	cx = game->world.cam_x;
-	cy = game->world.cam_y;
+	const Waver* waver;
+	const BM_Font* font;
+	Colour col;
+	char buf[32];
 
 	world = &game->world;
 	player = &world->player;
+	waver = &world->waver;
+	cx = world->cam_x;
+	cy = world->cam_y;
+
+	font = get_default_font();
 
 	update_waver(&world->waver, world);
+	ec = wave_enemy_count(&world->waver);
+
+	if (
+		world->enemies_killed >= ec &&
+		!game->want_next
+	) {
+		if (waver->idx >= get_wave_count() - 1) {
+			world->oom = -1;
+			game_change_state(game, game_state_dead);
+		}
+
+		game->wave_timer = 0;
+		game->want_next = 1;
+	}
+
+	if (game->want_next && game->wave_timer > 200) {
+		gameplay_next_wave(game);
+	}
 
 	update_player(player, world);
 	enemy_system(world);
 	debris_system(world);
 	bullet_system(world);
 	collision_system(world);
+	spawner_system(world);
 	animation_system(world);
 	update_camera(player, world);
 	render_map(&world->map, cx, cy);
 	sprite_system(world);
 
+	if (game->display_wave_text) {
+		col = make_colour(0x7f8dcf, 255);
+		buf[0] = 'W';
+		buf[1] = 'a';
+		buf[2] = 'v';
+		buf[3] = 'e';
+		buf[4] = ' ';
+		l = 5 + int_to_buf(world->waver.idx + 1, buf + 5);
+		rfont_text_col(
+			font,
+			renderer_w / 2 - (l * font->char_w) / 2,
+			renderer_h / 2,
+			buf,
+			col
+		);
+		game->display_wave_text--;
+	}
+
+	game->wave_timer++;
+
 	render_hud(game);
 
 	if (world->gmemory <= 0) {
@@ -202,18 +266,38 @@ static void credits_init(Game* game) {
 }
 
 static void credits_update(Game* game) {
+	Colour colour;
+	const BM_Font* font;
 
+	font = get_default_font();
+
+	if (
+		button_just_pressed(btn_shoot) ||
+		button_just_pressed(btn_jump)
+	) {
+		game_change_state(game, game_state_menu);
+	}
+
+	rfont_text(font, 0, 0,  "Programming: quou");
+	rfont_text(font, 0, 10, "Design:      quou");
+	rfont_text(font, 0, 20, "Sound:       quou");
+	rfont_text(font, 0, 30, "Graphics:    quou");
+	rfont_text(font, 0, 40, "Music:       drummyfish");
+	rfont_text(font, 0, 50, "Source Code: git.quou.xyz");
 }
 
 static void credits_deinit(Game* game) {
-
 }
 
 static void dead_init(Game* game) {
-	const BM_Font* font = get_default_font();
-	init_menu(&game->menu, game);
-	menu_add(&game->menu, "Restart", menu_restart, font);
-	menu_add(&game->menu, "Main Menu", menu_main_menu, font);
+	const BM_Font* font;
+	Menu* menu;
+
+	menu = &game->menu;
+	font = get_default_font();
+	init_menu(menu, game);
+	menu_add(menu, "Restart", menu_restart, font);
+	menu_add(menu, "Main Menu", menu_main_menu, font);
 }
 
 static void dead_update(Game* game) {
@@ -230,14 +314,22 @@ static void dead_update(Game* game) {
 	cy = game->world.cam_y;
 	render_map(&game->world.map, cx, cy);
 	sprite_system(&game->world);
+	render_hud(game);
 
-	if (world->oom) {
+	if (world->oom == 1) {
 		rfont_text(
 			font,
 			renderer_w / 2 - 65,
 			game->menu.y - 15,
 			"Out of Memory"
 		);
+	} else if (world->oom == -1) {
+		rfont_text(
+			font,
+			renderer_w / 2 - 35,
+			game->menu.y - 15,
+			"You win"
+		);
 	} else {
 		rfont_text(
 			font,
diff --git a/game.h b/game.h
index 6562673..66d9574 100644
--- a/game.h
+++ b/game.h
@@ -25,6 +25,7 @@ struct Game {
 	Menu menu;
 
 	World world;
+	int wave_timer, want_next, display_wave_text;
 };
 
 void game_init(Game* game, Game_State state);
@@ -33,4 +34,7 @@ void game_deinit(Game* game);
 
 void game_change_state(Game* game, Game_State state);
 
+void gameplay_next_wave(Game* menu);
+void gameplay_new(Game* game);
+
 #endif
diff --git a/game_config.h b/game_config.h
index 3b0f9d3..d0adbac 100644
--- a/game_config.h
+++ b/game_config.h
@@ -6,13 +6,13 @@
 #define player_max_hp 3
 #define player_invul_frames 50
 
-#define enemy_hit_frames 10
-
 #define skull_hp 3
 #define skull_speed (1 << fbits)
 
 #define skull_shoot_cooldown 15
 #define enemy_bullet_speed 10
+#define enemy_hit_frames 10
+#define enemy_min_distance (10 << fbits)
 
 #define map_width 32
 #define map_height 32
diff --git a/menu.c b/menu.c
index e4e82f5..ab3f7de 100644
--- a/menu.c
+++ b/menu.c
@@ -69,15 +69,17 @@ void update_menu(Menu* menu) {
 void render_menu(Menu* menu, const BM_Font* font) {
 	int i, x, y, cw, ch;
 	const Menu_Item* el;
+	Colour col;
 
 	y = menu->y;
 	cw = font->char_w;
 	ch = font->char_h;
+	col = make_colour(0xffde00, 255);
 
 	for (i = 0; i < menu->el_count; i++) {
 		el = &menu->els[i];
 		x = renderer_w / 2 - (cw * el->text_len) / 2;
-		rfont_text(font, x, y, el->text);
+		rfont_text_col(font, x, y, el->text, col);
 		if (i == menu->selected) {
 			rfont_char(font, x - cw, y, '>');
 		}
diff --git a/platform.c b/platform.c
index 940e57f..9bd5300 100644
--- a/platform.c
+++ b/platform.c
@@ -187,6 +187,10 @@ static int ww, wh, bb_pitch, ntw, nth;
 static int to_sleep, begin_time, end_time;
 static SDL_Joystick* joysticks[max_joysticks];
 
+int platform_get_time() {
+	return SDL_GetTicks();
+}
+
 static void fit_texture(int w, int h) {
 	ntw = renderer_w;
 	nth = renderer_h;
diff --git a/platform.h b/platform.h
index bd3bc21..0001f8b 100644
--- a/platform.h
+++ b/platform.h
@@ -7,4 +7,6 @@ void platform_log(const char* message, ...);
 void platform_err(const char* message, ...);
 void platform_abort(int code);
 
+int platform_get_time();
+
 #endif
diff --git a/player.c b/player.c
index ff3f53b..9075048 100644
--- a/player.c
+++ b/player.c
@@ -87,7 +87,7 @@ void update_player(Player* player, World* world) {
 	if (dx || dy) {
 		vec_nrmise(&dx, &dy);
 
-		if (dx) {
+		if (dx && !button_pressed(btn_shoot)) {
 			face = dx < 0 ? 0 : 1;
 		} else {
 			face = player->face;
@@ -117,13 +117,18 @@ void update_player(Player* player, World* world) {
 			animation_player_walk_left;
 	}
 
+	if (!button_pressed(btn_shoot)) {
+		player->sdx = player->ldx;
+		player->sdy = player->ldy;
+	}
+
 	if (button_pressed(btn_shoot) && player->shoot_countdown <= 0) {
 		new_player_bullet(
 			world,
 			pos->x,
 			pos->y,
-			(player->ldx * player_bullet_speed) >> fbits,
-			(player->ldy * player_bullet_speed) >> fbits,
+			(player->sdx * player_bullet_speed) >> fbits,
+			(player->sdy * player_bullet_speed) >> fbits,
 			100
 		);
 
diff --git a/player.h b/player.h
index 86a0426..1025eb8 100644
--- a/player.h
+++ b/player.h
@@ -6,7 +6,7 @@ struct World;
 typedef struct {
 	int entity;
 	int face;
-	int ldx, ldy;
+	int ldx, ldy, sdx, sdy;
 	int shoot_cooldown;
 	int shoot_countdown;
 	int hp;
diff --git a/spawner.c b/spawner.c
new file mode 100644
index 0000000..058a894
--- /dev/null
+++ b/spawner.c
@@ -0,0 +1,68 @@
+#include "animation.h"
+#include "components.h"
+#include "enemy.h"
+#include "world.h"
+
+int new_spawner(
+	World* world,
+	int x,
+	int y
+) {
+	Entity e;
+	CPosition* pos;
+	CSprite* sprite;
+	CAnimated* animated;
+
+	e = new_entity(world);
+	add_components(
+		world,
+		e,
+		ctype_sprite |
+		ctype_position |
+		ctype_animated |
+		ctype_spawner
+	);
+
+	pos = &world->positions[e];
+	sprite = &world->sprites[e];
+	animated = &world->animateds[e];
+
+	pos->x = x;
+	pos->y = y;
+
+	sprite->id = asset_id_usr;
+	sprite->rect = make_rect(0, 0, 16, 16);
+
+	animated->id = animation_spawn;
+	animated->frame = 0;
+	animated->timer = 0;
+
+	return e;
+}
+
+void spawner_system(World* world) {
+	int i;
+	const CPosition* pos;
+	const CAnimated* animated;
+	const Animation* animation;
+	unsigned bits;
+
+	for (i = 0; i < world->entity_count; i++) {
+		bits = world->bitmask[i];
+		if (
+			(bits & ctype_spawner) &&
+			(bits & ctype_position) &&
+			(bits & ctype_animated)
+		) {
+			pos = &world->positions[i];
+			animated = &world->animateds[i];
+
+			animation = get_animation(animated->id);
+
+			if (animated->frame == animation->frame_count - 1) {
+				new_skull(world, pos->x, pos->y);
+				destroy_entity(world, i);
+			}
+		}
+	}
+}
diff --git a/spawner.h b/spawner.h
new file mode 100644
index 0000000..65633c5
--- /dev/null
+++ b/spawner.h
@@ -0,0 +1,12 @@
+#ifndef spawner_h
+#define spawner_h
+
+struct World;
+
+int new_spawner(
+	struct World* world,
+	int x,
+	int y
+);
+
+#endif
diff --git a/systems.h b/systems.h
index 672e704..5108b80 100644
--- a/systems.h
+++ b/systems.h
@@ -6,8 +6,9 @@
 void animation_system(World* world);
 void bullet_system(World* world);
 void collision_system(World* world);
+void debris_system(World* world);
 void enemy_system(World* world);
+void spawner_system(World* world);
 void sprite_system(const World* world);
-void debris_system(World* world);
 
 #endif
diff --git a/wave.c b/wave.c
index 93b43f4..e6f4e44 100644
--- a/wave.c
+++ b/wave.c
@@ -1,9 +1,23 @@
-#include "enemy.h"
+#include "spawner.h"
 #include "standard.h"
 #include "wave.h"
 #include "world.h"
 
 static const Wave waves[] = {
+	{
+		{
+			{
+				20,
+				2,
+				{
+					{ 3, 5 },
+					{ 15, 15 },
+				}
+			}
+		},
+		1,
+		2
+	},
 	{
 		{
 			{
@@ -14,8 +28,19 @@ static const Wave waves[] = {
 					{ 15, 15 },
 				}
 			},
+			{
+				200,
+				4,
+				{
+					{ 30, 30 },
+					{ 15, 20 },
+					{ 23, 5 },
+					{ 6, 4 },
+				}
+			},
 		},
-		1
+		2,
+		6
 	}
 };
 
@@ -25,8 +50,18 @@ void init_waver(Waver* waver) {
 	waver->timer = 0;
 }
 
+void start_wave(struct World* world) {
+	world->wave_started = 1;
+	world->enemies_killed = 0;
+}
+
+int wave_enemy_count(const Waver* waver) {
+	return waves[waver->idx].kill_requirement;
+}
+
 void next_wave(Waver* waver) {
 	waver->idx++;
+	waver->sidx = 0;
 }
 
 void update_waver(Waver* waver, World* world) {
@@ -45,7 +80,7 @@ void update_waver(Waver* waver, World* world) {
 	if (waver->timer >= subwave->frame) {
 		for (i = 0; i < subwave->count; i++) {
 			spawn = &subwave->spawns[i];
-			new_skull(
+			new_spawner(
 				world,
 				(spawn->x * map_tile_size) << fbits,
 				(spawn->y * map_tile_size) << fbits
@@ -54,3 +89,7 @@ void update_waver(Waver* waver, World* world) {
 		waver->sidx++;
 	}
 }
+
+int get_wave_count() {
+	return sizeof waves / sizeof *waves;
+}
diff --git a/wave.h b/wave.h
index 3d40981..2f642fe 100644
--- a/wave.h
+++ b/wave.h
@@ -22,6 +22,7 @@ typedef struct {
 typedef struct {
 	Subwave subwaves[wave_max_subwaves];
 	int subwave_count;
+	int kill_requirement;
 } Wave;
 
 struct Waver {
@@ -30,7 +31,10 @@ struct Waver {
 };
 
 void init_waver(Waver* waver);
+void start_wave(struct World* world);
+int wave_enemy_count(const Waver* waver);
 void next_wave(Waver* waver);
 void update_waver(Waver* waver, struct World* world);
+int get_wave_count();
 
 #endif
diff --git a/world.h b/world.h
index 6749dbc..f6a4d07 100644
--- a/world.h
+++ b/world.h
@@ -32,7 +32,8 @@ struct World {
 	Waver waver;
 
 	int cam_x, cam_y;
-	int enemy_count;
+	int enemy_count, wave_started, enemies_killed;
+	int has_less_collision, has_less_hud;
 	int gmemory, oom;
 };
 
-- 
cgit v1.2.3-54-g00ecf