From 40eb179043b77f011fb1048c386ee187f64569d0 Mon Sep 17 00:00:00 2001
From: quou <quou@disroot.org>
Date: Sun, 7 May 2023 09:37:45 +1000
Subject: Add waves and some more polish.

---
 Makefile             |   1 +
 animation.c          |  13 ++++++++++++
 animation.h          |   3 ++-
 collision_system.c   |   2 +-
 config.h             |   3 +++
 enemy.c              |   2 ++
 fx.c                 |  35 ++++++++++++++++++++++++++++----
 fx.h                 |   6 ++++++
 game.c               |   7 ++++---
 game_config.h        |   1 +
 intermediate/usr.bmp | Bin 75378 -> 75378 bytes
 player.c             |  36 ++++++++++++++++++++++++++++++++-
 player.h             |   7 ++++++-
 wave.c               |  56 +++++++++++++++++++++++++++++++++++++++++++++++++++
 wave.h               |  36 +++++++++++++++++++++++++++++++++
 world.c              |   3 +++
 world.h              |   4 ++++
 17 files changed, 204 insertions(+), 11 deletions(-)
 create mode 100644 wave.c
 create mode 100644 wave.h

diff --git a/Makefile b/Makefile
index 44b9fa3..715391a 100644
--- a/Makefile
+++ b/Makefile
@@ -106,6 +106,7 @@ sources = \
   sprite.c \
   sprite_system.c \
   standard.c \
+  wave.c \
   world.c \
 
 
diff --git a/animation.c b/animation.c
index a0fea2c..ff944eb 100644
--- a/animation.c
+++ b/animation.c
@@ -66,6 +66,19 @@ static const Animation animations[] = {
 		},
 		7,
 		1
+	},
+	/* animation_heart_break */
+	{
+		{
+			{ 72,  72, 7, 7 },
+			{ 79,  72, 7, 7 },
+			{ 86,  72, 7, 7 },
+			{ 93,  72, 7, 7 },
+			{ 100, 72, 7, 7 },
+			{ 107, 72, 7, 7 }
+		},
+		6,
+		3
 	}
 };
 
diff --git a/animation.h b/animation.h
index e132202..0a36043 100644
--- a/animation.h
+++ b/animation.h
@@ -10,7 +10,8 @@ typedef enum {
 	animation_player_idle_left,
 	animation_player_idle_right,
 	animation_enemy_bullet_explode,
-	animation_player_bullet_explode
+	animation_player_bullet_explode,
+	animation_heart_break
 } Animation_ID;
 
 typedef struct {
diff --git a/collision_system.c b/collision_system.c
index cf1378c..ed05817 100644
--- a/collision_system.c
+++ b/collision_system.c
@@ -47,7 +47,7 @@ static void handle_bullet_vs_player(
 
 	pos = &world->positions[bullet];
 
-	player_take_damage(&world->player, 1);
+	player_take_damage(world, &world->player, 1);
 	destroy_entity(world, bullet);
 	new_enemy_bullet_explosion(world, pos->x, pos->y);
 }
diff --git a/config.h b/config.h
index 51a559a..4465d68 100644
--- a/config.h
+++ b/config.h
@@ -21,4 +21,7 @@
 
 #define menu_max_elements 4
 
+#define wave_max_subwaves 8
+#define wave_max_spawns 16
+
 #endif
diff --git a/enemy.c b/enemy.c
index 332a769..710a280 100644
--- a/enemy.c
+++ b/enemy.c
@@ -42,6 +42,8 @@ Entity new_skull(World* world, int x, int y) {
 	enemy->hp = skull_hp;
 	enemy->shoot_timer = 0;
 
+	world->enemy_count++;
+
 	return e;
 }
 
diff --git a/fx.c b/fx.c
index 53670b4..8e0e41f 100644
--- a/fx.c
+++ b/fx.c
@@ -6,7 +6,8 @@ static int new_fx(
 	World* world,
 	int x,
 	int y,
-	Animation_ID anim
+	Animation_ID anim,
+	Asset_ID bitmap
 ) {
 	Entity e;
 	CPosition* pos;
@@ -30,7 +31,7 @@ static int new_fx(
 	pos->x = x;
 	pos->y = y;
 
-	sprite->id = asset_id_bullet;
+	sprite->id = bitmap;
 	sprite->rect = make_rect(0, 0, 16, 16);
 
 	animated->id = anim;
@@ -45,7 +46,13 @@ int new_enemy_bullet_explosion(
 	int x,
 	int y
 ) {
-	return new_fx(world, x, y, animation_enemy_bullet_explode);
+	return new_fx(
+		world,
+		x,
+		y,
+		animation_enemy_bullet_explode,
+		asset_id_bullet
+	);
 }
 
 int new_player_bullet_explosion(
@@ -53,5 +60,25 @@ int new_player_bullet_explosion(
 	int x,
 	int y
 ) {
-	return new_fx(world, x, y, animation_player_bullet_explode);
+	return new_fx(
+		world,
+		x,
+		y,
+		animation_player_bullet_explode,
+		asset_id_bullet
+	);
+}
+
+int new_heart_break(
+	struct World* world,
+	int x,
+	int y
+) {
+	return new_fx(
+		world,
+		x,
+		y,
+		animation_heart_break,
+		asset_id_usr
+	);
 }
diff --git a/fx.h b/fx.h
index dff8c47..91ab4e6 100644
--- a/fx.h
+++ b/fx.h
@@ -17,4 +17,10 @@ int new_player_bullet_explosion(
 	int y
 );
 
+int new_heart_break(
+	struct World* world,
+	int x,
+	int y
+);
+
 #endif
diff --git a/game.c b/game.c
index f5edb8a..b68d85a 100644
--- a/game.c
+++ b/game.c
@@ -45,8 +45,7 @@ static void menu_init(Game* game) {
 	font = get_default_font();
 
 	init_menu(&game->menu, game);
-	menu_add(&game->menu, "New", menu_new, font);
-	menu_add(&game->menu, "Continue", 0, font);
+	menu_add(&game->menu, "Play", menu_new, font);
 	menu_add(&game->menu, "Credits", menu_credits, font);
 	menu_add(&game->menu, "Quit", menu_quit, font);
 }
@@ -104,7 +103,7 @@ static void gameplay_init(Game* game) {
 	init_world(&game->world);
 	init_map(&world->map, world);
 	init_player(&world->player, world);
-	new_skull(world, 0, 0);
+	init_waver(&world->waver);
 
 	world->gmemory = gmemory_max;
 }
@@ -174,6 +173,8 @@ static void gameplay_update(Game* game) {
 	world = &game->world;
 	player = &world->player;
 
+	update_waver(&world->waver, world);
+
 	update_player(player, world);
 	enemy_system(world);
 	debris_system(world);
diff --git a/game_config.h b/game_config.h
index 5094cf8..4e955a9 100644
--- a/game_config.h
+++ b/game_config.h
@@ -4,6 +4,7 @@
 #define player_move_speed (3 << fbits)
 #define player_bullet_speed (10 << fbits)
 #define player_max_hp 3
+#define player_invul_frames 50
 
 #define skull_hp 3
 #define skull_speed (1 << fbits)
diff --git a/intermediate/usr.bmp b/intermediate/usr.bmp
index b8bcd09..c0eecc2 100644
Binary files a/intermediate/usr.bmp and b/intermediate/usr.bmp differ
diff --git a/player.c b/player.c
index 7135557..ff3f53b 100644
--- a/player.c
+++ b/player.c
@@ -1,6 +1,7 @@
 #include "animation.h"
 #include "bullet.h"
 #include "components.h"
+#include "fx.h"
 #include "game.h"
 #include "game_config.h"
 #include "input.h"
@@ -129,6 +130,22 @@ void update_player(Player* player, World* world) {
 		player->shoot_countdown = player->shoot_cooldown;
 	}
 
+	if (player->invul) {
+		player->invul--;
+		player->invul_counter++;
+
+		if (player->invul_counter > 3) {
+			if (world->bitmask[e] & ctype_sprite) {
+				world->bitmask[e] &= ~ctype_sprite;
+			} else {
+				world->bitmask[e] |= ctype_sprite;
+			}
+			player->invul_counter = 0;
+		}
+	} else {
+		world->bitmask[e] |= ctype_sprite;
+	}
+
 	if (player->hp <= 0) {
 		world->oom = 0;
 		game_change_state(&game, game_state_dead);
@@ -137,8 +154,25 @@ void update_player(Player* player, World* world) {
 	player->shoot_countdown--;
 }
 
-void player_take_damage(Player* player, int dmg) {
+void player_take_damage(
+	World* world,
+	Player* player,
+	int dmg
+) {
+	const CPosition* pos;
+
+	pos = &world->positions[player->entity];
+
+	if (player->invul) { return; }
+
 	player->hp -= dmg;
+	player->invul = player_invul_frames;
+	player->invul_counter = 0;
+	new_heart_break(
+		world,
+		pos->x + (4 << fbits),
+		pos->y - (8 << fbits)
+	);
 }
 
 void update_camera(Player* player, World* world) {
diff --git a/player.h b/player.h
index 0fb3e4d..86a0426 100644
--- a/player.h
+++ b/player.h
@@ -10,11 +10,16 @@ typedef struct {
 	int shoot_cooldown;
 	int shoot_countdown;
 	int hp;
+	int invul, invul_counter;
 } Player;
 
 void init_player(Player* player, struct World* world);
 void update_player(Player* player, struct World* world);
-void player_take_damage(Player* player, int dmg);
+void player_take_damage(
+	struct World* world,
+	Player* player,
+	int dmg
+);
 void update_camera(Player* player, struct World* world);
 
 #endif
diff --git a/wave.c b/wave.c
new file mode 100644
index 0000000..93b43f4
--- /dev/null
+++ b/wave.c
@@ -0,0 +1,56 @@
+#include "enemy.h"
+#include "standard.h"
+#include "wave.h"
+#include "world.h"
+
+static const Wave waves[] = {
+	{
+		{
+			{
+				20,
+				2,
+				{
+					{ 3, 5 },
+					{ 15, 15 },
+				}
+			},
+		},
+		1
+	}
+};
+
+void init_waver(Waver* waver) {
+	waver->idx   = 0;
+	waver->sidx  = 0;
+	waver->timer = 0;
+}
+
+void next_wave(Waver* waver) {
+	waver->idx++;
+}
+
+void update_waver(Waver* waver, World* world) {
+	const Wave* wave;
+	const Subwave* subwave;
+	const Wave_Spawn* spawn;
+	int i;
+
+	wave = &waves[waver->idx];
+	subwave = &wave->subwaves[waver->sidx];
+
+	if (waver->sidx >= wave->subwave_count) { return; }
+
+	waver->timer++;
+
+	if (waver->timer >= subwave->frame) {
+		for (i = 0; i < subwave->count; i++) {
+			spawn = &subwave->spawns[i];
+			new_skull(
+				world,
+				(spawn->x * map_tile_size) << fbits,
+				(spawn->y * map_tile_size) << fbits
+			);
+		}
+		waver->sidx++;
+	}
+}
diff --git a/wave.h b/wave.h
new file mode 100644
index 0000000..3d40981
--- /dev/null
+++ b/wave.h
@@ -0,0 +1,36 @@
+#ifndef wave_h
+#define wave_h
+
+#include "config.h"
+
+/* TODO: Different enemies. */
+
+typedef struct Waver Waver;
+
+typedef void (*Wave_Finish)(Waver* waver, int last);
+
+typedef struct {
+	int x, y;
+} Wave_Spawn;
+
+typedef struct {
+	int frame;
+	int count;
+	Wave_Spawn spawns[wave_max_spawns];
+} Subwave;
+
+typedef struct {
+	Subwave subwaves[wave_max_subwaves];
+	int subwave_count;
+} Wave;
+
+struct Waver {
+	int idx, sidx;
+	int timer;
+};
+
+void init_waver(Waver* waver);
+void next_wave(Waver* waver);
+void update_waver(Waver* waver, struct World* world);
+
+#endif
diff --git a/world.c b/world.c
index 4995351..3ee369d 100644
--- a/world.c
+++ b/world.c
@@ -36,6 +36,9 @@ Entity new_entity(World* world) {
 
 void destroy_entity(World* world, Entity e) {
 	world->recycle_bin[world->recycle_bin_count++] = e;
+	if (world->bitmask[e] & ctype_enemy) {
+		world->enemy_count--;
+	}
 	world->bitmask[e] = 0;
 	world->gmemory++;
 }
diff --git a/world.h b/world.h
index 5f324a8..6749dbc 100644
--- a/world.h
+++ b/world.h
@@ -5,11 +5,13 @@
 #include "config.h"
 #include "map.h"
 #include "player.h"
+#include "wave.h"
 
 typedef int Entity;
 
 typedef struct World World;
 
+/* Keep as POD. */
 struct World {
 	int entity_count;
 	int recycle_bin_count;
@@ -27,8 +29,10 @@ struct World {
 
 	Player player;
 	Map map;
+	Waver waver;
 
 	int cam_x, cam_y;
+	int enemy_count;
 	int gmemory, oom;
 };
 
-- 
cgit v1.2.3-54-g00ecf