From 2ab411c4b8855d11d48454a93262e8eae3ba7fc7 Mon Sep 17 00:00:00 2001 From: quou Date: Sat, 6 May 2023 09:02:04 +1000 Subject: Menus, game over, dying, etc. --- Makefile | 2 + asset.c | 14 ++++ asset.h | 1 + collision_system.c | 1 + config.h | 4 +- game.c | 200 +++++++++++++++++++++++++++++++++++++++++++++++++++ game.h | 36 ++++++++++ game_config.h | 1 + intermediate/usr.bmp | Bin 67422 -> 68178 bytes main.c | 24 ++----- menu.c | 86 ++++++++++++++++++++++ menu.h | 35 +++++++++ player.c | 14 +++- player.h | 2 + render.c | 92 +++++++++++++++++++++++- render.h | 18 +++-- sprite.c | 14 ++++ sprite.h | 5 +- world.c | 5 +- 19 files changed, 522 insertions(+), 32 deletions(-) create mode 100644 game.c create mode 100644 game.h create mode 100644 menu.c create mode 100644 menu.h diff --git a/Makefile b/Makefile index 4fe54fd..f4d21cf 100644 --- a/Makefile +++ b/Makefile @@ -92,8 +92,10 @@ sources = \ collision_system.c \ enemy.c \ fx.c \ + game.c \ main.c \ malware.c \ + menu.c \ platform.c \ player.c \ rect.c \ diff --git a/asset.c b/asset.c index 5ae5419..5db8a20 100644 --- a/asset.c +++ b/asset.c @@ -16,6 +16,8 @@ static struct { FILE* pack; int pack_count; + BM_Font default_font; + unsigned char memory[asset_memory]; int memory_ptr; @@ -113,8 +115,20 @@ void load_assets() { #undef gi fclose(pack); + + init_font( + &asset_manager.default_font, + get_bitmap(asset_id_usr), + 10, + 10, + 16 + ); } const Bitmap* get_bitmap(Asset_ID id) { return &asset_manager.bitmaps[id]; } + +const BM_Font* get_default_font() { + return &asset_manager.default_font; +} diff --git a/asset.h b/asset.h index 8c6e201..23b0893 100644 --- a/asset.h +++ b/asset.h @@ -16,5 +16,6 @@ typedef enum { void load_assets(); const Bitmap* get_bitmap(Asset_ID id); +const BM_Font* get_default_font(); #endif diff --git a/collision_system.c b/collision_system.c index abd5dc9..68e3b09 100644 --- a/collision_system.c +++ b/collision_system.c @@ -15,6 +15,7 @@ static void handle_bullet_vs_player( pos = &world->positions[bullet]; + player_take_damage(&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 063781b..ff95ba4 100644 --- a/config.h +++ b/config.h @@ -18,7 +18,7 @@ #define animation_max_frames 8 #define max_entities 256 -#define max_player_bullets 32 -#define max_enemy_bullets 256 + +#define menu_max_elements 4 #endif diff --git a/game.c b/game.c new file mode 100644 index 0000000..73d062b --- /dev/null +++ b/game.c @@ -0,0 +1,200 @@ +#include "asset.h" +#include "enemy.h" +#include "game.h" +#include "platform.h" +#include "sprite.h" +#include "systems.h" + +static void menu_new(Menu* menu) { + Game* game; + + game = menu->ptr; + game_change_state(game, game_state_game); +} + +static void menu_restart(Menu* menu) { + Game* game; + + game = menu->ptr; + game_change_state(game, game_state_game); +} + +static void menu_main_menu(Menu* menu) { + Game* game; + + game = menu->ptr; + game_change_state(game, game_state_menu); +} + +static void menu_quit(Menu* menu) { + (void)menu; + platform_quit(); +} + +static void menu_credits(Menu* menu) { + Game* game; + + game = menu->ptr; + game_change_state(game, game_state_credits); +} + +static void menu_init(Game* game) { + const BM_Font* font; + + 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, "Credits", menu_credits, font); + menu_add(&game->menu, "Quit", menu_quit, font); +} + +static void menu_update(Game* game) { + const Sprite* msg; + const Bitmap* bmp; + int h, y; + + update_menu(&game->menu); + + h = game->menu.h; + y = game->menu.y; + + msg = get_sprite(sprite_author); + bmp = get_bitmap(msg->bitmap); + render_bitmap( + bmp, + renderer_w / 2 - msg->rect.w / 2, + renderer_h - 26, + &msg->rect + ); + + msg = get_sprite(sprite_free); + bmp = get_bitmap(msg->bitmap); + render_bitmap( + bmp, + renderer_w / 2 - msg->rect.w / 2, + renderer_h - 15, + &msg->rect + ); + + render_menu(&game->menu, get_default_font()); +} + +static void menu_deinit(Game* game) { + +} + +static void gameplay_init(Game* game) { + init_world(&game->world); + + init_player(&game->world.player, &game->world); + + new_skull(&game->world, 0, 0); +} + +static void gameplay_update(Game* game) { + update_player(&game->world.player, &game->world); + enemy_system(&game->world); + bullet_system(&game->world); + collision_system(&game->world); + animation_system(&game->world); + sprite_system(&game->world); +} + +static void gameplay_deinit(Game* game) { + +} + +static void credits_init(Game* game) { + +} + +static void credits_update(Game* game) { + +} + +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); +} + +static void dead_update(Game* game) { + const BM_Font* font; + + font = get_default_font(); + + update_menu(&game->menu); + + sprite_system(&game->world); + + rfont_text( + font, + renderer_w / 2 - 40, + game->menu.y - 15, + "You died" + ); + render_menu(&game->menu, get_default_font()); +} + +static void dead_deinit(Game* game) { + +} + +static const Game_State_Fns game_state_fns[] = { + /* game_state_menu */ + { + menu_init, + menu_update, + menu_deinit + }, + /* game_state_game */ + { + gameplay_init, + gameplay_update, + gameplay_deinit + }, + /* game_state_credits */ + { + credits_init, + credits_update, + credits_deinit + }, + /* game_state_dead */ + { + dead_init, + dead_update, + dead_deinit + } +}; + +static const Game_State_Fns* get_state_fns(Game_State state) { + return &game_state_fns[state]; +} + +void game_init(Game* game, Game_State state) { + game->state = state; + game->fns = get_state_fns(state); + game->fns->state_init(game); +} + +void game_update(Game* game) { + game->fns->state_update(game); +} + +void game_deinit(Game* game) { + game->fns->state_deinit(game); +} + +void game_change_state(Game* game, Game_State state) { + game->fns->state_deinit(game); + game->state = state; + game->fns = get_state_fns(state); + game->fns->state_init(game); +} diff --git a/game.h b/game.h new file mode 100644 index 0000000..6562673 --- /dev/null +++ b/game.h @@ -0,0 +1,36 @@ +#ifndef game_h +#define game_h + +#include "world.h" +#include "menu.h" + +typedef enum { + game_state_menu = 0, + game_state_game, + game_state_credits, + game_state_dead +} Game_State; + +typedef struct Game Game; + +typedef struct { + void (*state_init)(Game* game); + void (*state_update)(Game* game); + void (*state_deinit)(Game* game); +} Game_State_Fns; + +struct Game { + const Game_State_Fns* fns; + Game_State state; + Menu menu; + + World world; +}; + +void game_init(Game* game, Game_State state); +void game_update(Game* game); +void game_deinit(Game* game); + +void game_change_state(Game* game, Game_State state); + +#endif diff --git a/game_config.h b/game_config.h index 0c97a8d..b2d569f 100644 --- a/game_config.h +++ b/game_config.h @@ -3,6 +3,7 @@ #define player_move_speed (3 << fbits) #define player_bullet_speed (10 << fbits) +#define player_max_hp 3 #define skull_hp 3 #define skull_speed (1 << fbits) diff --git a/intermediate/usr.bmp b/intermediate/usr.bmp index e5e8185..7735e60 100644 Binary files a/intermediate/usr.bmp and b/intermediate/usr.bmp differ diff --git a/main.c b/main.c index 9d2aff8..0e26dd3 100644 --- a/main.c +++ b/main.c @@ -1,12 +1,7 @@ -#include "asset.h" -#include "player.h" -#include "render.h" +#include "game.h" #include "standard.h" -#include "systems.h" -#include "world.h" -#include "enemy.h" -World world; +Game game; void on_init(int argc, char** argv) { seed_rng(500); @@ -15,23 +10,14 @@ void on_init(int argc, char** argv) { load_assets(); - init_world(&world); - - init_player(&world.player, &world); - - new_skull(&world, 0, 0); + game_init(&game, game_state_menu); } void on_update() { renderer_begin_frame(); - update_player(&world.player, &world); - enemy_system(&world); - bullet_system(&world); - collision_system(&world); - animation_system(&world); - sprite_system(&world); + game_update(&game); } void on_deinit() { - + game_deinit(&game); } diff --git a/menu.c b/menu.c new file mode 100644 index 0000000..e4e82f5 --- /dev/null +++ b/menu.c @@ -0,0 +1,86 @@ +#include "asset.h" +#include "input.h" +#include "menu.h" + +#ifdef DEBUG +#include "error.h" +#include "platform.h" +#endif + +void init_menu(Menu* menu, void* ptr) { + menu->el_count = 0; + menu->selected = 0; + menu->y = renderer_h / 2; + menu->h = 0; + menu->ptr = ptr; +} + +void menu_add( + Menu* menu, + const char* text, + Menu_Event on_select, + const BM_Font* font +) { + Menu_Item* it; + int i; + +#ifdef DEBUG + if (menu->el_count >= menu_max_elements) { + platform_err("Too many menu elements.\n"); + platform_abort(error_gameplay_error); + } +#endif + + it = &menu->els[menu->el_count++]; + it->on_select = on_select; + it->text_len = 0; + for (i = 0; text[i]; i++) { + it->text[i] = text[i]; + it->text_len++; + } it->text[i] = 0; + + menu->h = font->char_h * menu->el_count; + menu->y = renderer_h / 2 - (menu->h) / 2; +} + +void update_menu(Menu* menu) { + if (button_just_pressed(btn_dpad_down)) { + menu->selected++; + if (menu->selected >= menu->el_count) { + menu->selected = 0; + } + } + + if (button_just_pressed(btn_dpad_up)) { + menu->selected--; + if (menu->selected < 0) { + menu->selected = menu->el_count - 1; + } + } + + if ( + button_just_pressed(btn_jump) || + button_just_pressed(btn_shoot) + ) { + menu->els[menu->selected].on_select(menu); + } +} + +void render_menu(Menu* menu, const BM_Font* font) { + int i, x, y, cw, ch; + const Menu_Item* el; + + y = menu->y; + cw = font->char_w; + ch = font->char_h; + + 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); + if (i == menu->selected) { + rfont_char(font, x - cw, y, '>'); + } + y += ch; + } +} diff --git a/menu.h b/menu.h new file mode 100644 index 0000000..52403f1 --- /dev/null +++ b/menu.h @@ -0,0 +1,35 @@ +#ifndef menu_h +#define menu_h + +#include "config.h" +#include "render.h" + +typedef struct Menu Menu; + +typedef void (*Menu_Event)(Menu* menu); + +typedef struct { + Menu_Event on_select; + int text_len; + char text[20]; +} Menu_Item; + +struct Menu { + Menu_Item els[menu_max_elements]; + int el_count; + int selected; + int y, h; + void* ptr; +}; + +void init_menu(Menu* menu, void* ptr); +void menu_add( + Menu* menu, + const char* text, + Menu_Event on_select, + const BM_Font* font +); +void update_menu(Menu* menu); +void render_menu(Menu* menu, const BM_Font* font); + +#endif diff --git a/player.c b/player.c index b2dbf29..592ef4a 100644 --- a/player.c +++ b/player.c @@ -1,11 +1,14 @@ #include "animation.h" +#include "bullet.h" #include "components.h" +#include "game.h" #include "game_config.h" #include "input.h" #include "player.h" #include "standard.h" #include "world.h" -#include "bullet.h" + +extern Game game; void init_player(Player* player, World* world) { CSprite* sprite; @@ -48,6 +51,7 @@ void init_player(Player* player, World* world) { player->face = 0; player->shoot_cooldown = 15; player->shoot_countdown = 0; + player->hp = player_max_hp; } void update_player(Player* player, World* world) { @@ -124,5 +128,13 @@ void update_player(Player* player, World* world) { player->shoot_countdown = player->shoot_cooldown; } + if (player->hp <= 0) { + game_change_state(&game, game_state_dead); + } + player->shoot_countdown--; } + +void player_take_damage(Player* player, int dmg) { + player->hp -= dmg; +} diff --git a/player.h b/player.h index da1605e..0e84b9e 100644 --- a/player.h +++ b/player.h @@ -9,9 +9,11 @@ typedef struct { int ldx, ldy; int shoot_cooldown; int shoot_countdown; + int hp; } 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); #endif diff --git a/render.c b/render.c index 578abef..b2a59e2 100644 --- a/render.c +++ b/render.c @@ -73,7 +73,7 @@ Colour blend_mod(Colour dst, Colour src, Colour mod) { void init_font( BM_Font* font, - Bitmap* bmp, + const Bitmap* bmp, int char_w, int char_h, int chars_across @@ -84,28 +84,65 @@ void init_font( font->chars_across = chars_across; } -void rfont_char(int x, int y, char c) { +void rfont_char(const BM_Font* font, int x, int y, char c) { + Rectangle rect; + c -= ' '; + + rect.x = (c % font->chars_across) * font->char_w; + rect.y = (c / font->chars_across) * font->char_h; + rect.w = font->char_w; + rect.h = font->char_h; + + render_bitmap(font->bmp, x, y, &rect); } -void rfont_char_col(int x, int y, char c, Colour colour) { +void rfont_char_col( + const BM_Font* font, + int x, + int y, + char c, + Colour colour +) { + Rectangle rect; + + c -= ' '; + + rect.x = (c % font->chars_across) * font->char_w; + rect.y = (c / font->chars_across) * font->char_h; + rect.w = font->char_w; + rect.h = font->char_h; + render_bitmap_col(font->bmp, x, y, &rect, colour); } void rfont_text( + const BM_Font* font, int x, int y, const char* text ) { + const char* c; + + for (c = text; *c; c++) { + rfont_char(font, x, y, *c); + x += font->char_w; + } } void rfont_text_col( + const BM_Font* font, int x, int y, const char* text, Colour colour ) { + const char* c; + for (c = text; *c; c++) { + rfont_char_col(font, x, y, *c, colour); + x += font->char_w; + } } void render_bitmap( @@ -173,5 +210,54 @@ void render_bitmap_col( const Rectangle* rect, Colour colour ) { + int i, j, stride, sstride, n; + Colour* dst; + const Colour* src; + Rectangle sub; + sub = *rect; + + if (sub.w <= 0) { return; } + if (sub.h <= 0) { return; } + if (sub.w > bitmap->w) { return; } + if (sub.h > bitmap->h) { return; } + + if ((n = -x) > 0) { + sub.w -= n; + sub.x += n; + x += n; + } + + if ((n = -y) > 0) { + sub.h -= n; + sub.y += n; + y += n; + } + + if ((n = x + sub.w - renderer_w) > 0) { + sub.w -= n; + } + + if ((n = y + sub.h - renderer_h) > 0) { + sub.h -= n; + } + + if (sub.w <= 0) { return; } + if (sub.h <= 0) { return; } + + dst = renderer.pixels + (x + y * renderer_w); + src = bitmap->pixels + (sub.x + sub.y * bitmap->w); + + stride = renderer_w - sub.w; + sstride = bitmap->w - sub.w; + + for (i = 0; i < sub.h; i++) { + for (j = 0; j < sub.w; j++) { + *dst = blend_mod(*dst, *src, colour); + dst++; + src++; + } + dst += stride; + src += sstride; + } } diff --git a/render.h b/render.h index d63297e..be03f02 100644 --- a/render.h +++ b/render.h @@ -24,28 +24,36 @@ void init_bitmap( int h ); -typedef struct { - Bitmap* bmp; +typedef struct BM_Font { + const Bitmap* bmp; int char_w, char_h; int chars_across; } BM_Font; void init_font( BM_Font* font, - Bitmap* bmp, + const Bitmap* bmp, int char_w, int char_h, int chars_across ); -void rfont_char(int x, int y, char c); -void rfont_char_col(int x, int y, char c, Colour colour); +void rfont_char(const BM_Font* font, int x, int y, char c); +void rfont_char_col( + const BM_Font* font, + int x, + int y, + char c, + Colour colour +); void rfont_text( + const BM_Font* font, int x, int y, const char* text ); void rfont_text_col( + const BM_Font* font, int x, int y, const char* text, diff --git a/sprite.c b/sprite.c index c14f717..ca72f44 100644 --- a/sprite.c +++ b/sprite.c @@ -60,6 +60,16 @@ static const Sprite sprites[] = { { asset_id_bullet, { 18, 0, 5, 5 } + }, + /* sprite_author */ + { + asset_id_usr, + { 0, 70, 70, 10 } + }, + /* sprite_free */ + { + asset_id_usr, + { 0, 80, 171, 10 } } }; @@ -70,3 +80,7 @@ void init_csprite(CSprite* sprite, Sprite_ID id) { sprite->id = s->bitmap; sprite->rect = s->rect; } + +const Sprite* get_sprite(Sprite_ID id) { + return &sprites[id]; +} diff --git a/sprite.h b/sprite.h index cf9d6e8..cdc5e28 100644 --- a/sprite.h +++ b/sprite.h @@ -17,7 +17,9 @@ typedef enum { sprite_skull_debris_3, sprite_skull_debris_4, sprite_skull_debris_5, - sprite_enemy_bullet + sprite_enemy_bullet, + sprite_author, + sprite_free } Sprite_ID; typedef struct { @@ -26,5 +28,6 @@ typedef struct { } Sprite; void init_csprite(CSprite* sprite, Sprite_ID id); +const Sprite* get_sprite(Sprite_ID id); #endif diff --git a/world.c b/world.c index 920484c..39673d4 100644 --- a/world.c +++ b/world.c @@ -3,7 +3,10 @@ #include "error.h" void init_world(World* world) { - world->entity_count = 0; + int i, s = sizeof world; + for (i = 0; i < s; i++) { + ((char*)world)[i] = 0; + } } Entity new_entity(World* world) { -- cgit v1.2.3-54-g00ecf