From aced4c28be118bc2151503b377b8bcc33f2447c3 Mon Sep 17 00:00:00 2001 From: Lephenixnoir Date: Sun, 13 Feb 2022 10:03:14 +0100 Subject: [PATCH] basic main menu with transitions --- CMakeLists.txt | 4 ++ assets-cg/fxconv-metadata.txt | 4 -- assets-cg/menu_arrows.png | Bin 0 -> 1055 bytes assets-cg/menu_title.png | Bin 0 -> 1711 bytes src/main.c | 23 ++---- src/menu.c | 131 ++++++++++++++++++++++++++++++++++ src/menu.h | 9 +++ src/render.c | 25 +++---- src/render.h | 8 +++ 9 files changed, 169 insertions(+), 35 deletions(-) create mode 100644 assets-cg/menu_arrows.png create mode 100644 assets-cg/menu_title.png create mode 100644 src/menu.c create mode 100644 src/menu.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 0c8dd7d..f0c2b7f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -22,6 +22,7 @@ set(SOURCES src/geometry.c src/main.c src/map.c + src/menu.c src/pathfinding.c src/player.c src/render.c @@ -44,6 +45,9 @@ set(ASSETS assets-cg/levels/lv1.txt assets-cg/levels/lv2.tmx assets-cg/levels/lv2.txt + # Menu + assets-cg/menu_title.png + assets-cg/menu_arrows.png # HUD assets-cg/hud.png assets-cg/hud_life.png diff --git a/assets-cg/fxconv-metadata.txt b/assets-cg/fxconv-metadata.txt index f7d009b..1883b2f 100644 --- a/assets-cg/fxconv-metadata.txt +++ b/assets-cg/fxconv-metadata.txt @@ -1,15 +1,11 @@ *.png: type: bopti-image name_regex: (.*)\.png img_\1 - profile: p8 - -hud*.png: profile: p4 hud_xp.ase: custom-type: aseprite-anim name: frames_hud_xp - profile: p4 center: 6, 8 next: Idle=Idle, Shine=Idle, Explode=Idle diff --git a/assets-cg/menu_arrows.png b/assets-cg/menu_arrows.png new file mode 100644 index 0000000000000000000000000000000000000000..370a817c8a9ed0c46ee54ea82915f9e277e33963 GIT binary patch literal 1055 zcmV+)1mOFLP)EX>4Tx04R}tkv&MmKpe$iQ$>-gh#f>6GE^rkq9Tr3g(6f4wL+^7CYOFelZGV4 z#ZhoAIQX$xb#QUk)xlK|1V2C=otzY1q{ROvg%&X$9QWhhy~o`h822neAEeHfFOWz0!Z0>0zx9s$1IMR}J0xj#p@nza}Z5Q%4*VcNtS#M7I$ z!FiuJ%nGtfd`>)S(glehxvqHp#yRh@z%#>UDmhOaCKmH8th6vIm>TgEaYWU0$`{fe ztDLtuYo!Wn+>^gBl+{<3xlVHkaV%m95=1DdpokJ|L}}GYv5=(wn2&$Z^-JVZ$W;U* z#{$aGAiI9>Klt6Pm7ARKl0q?{{l#%UhJlV0Yqi@Op{kK5Zn%7%nAEysMin>bN00)P_ zNS?CSecs*K+S|Wp8vXqMQciN3Ffd|300006VoOIv0RI600RN!9r;`8x010qNS#tmY zE+YT{E+YYWr9XB6000McNliru<^l{5HyTZxMA-lU02y>eSad^gZEa<4bO1wgWnpw> zWFU8GbZ8()Nlj2!fese{00H+&L_t(&-qo7jvcn(eaj~y1JTd>AMK6>(UL*0~fR2x_Q4gqok zz0PHvQ=`g36%+>$cPNd348XA~l+^oXsikxPVX&t=6r*Ih&(@({=*Cb*1(=jl77Irf z3LB2>44=gqTI?CaSXp2~;ghXGPYHKsg8>J-a*Cl!VP*4t{x0#J-v#sAEeEN9Ql+5P z^H)F&^#}`!)$u4C>vJqP!h*76|Kfod*z5djgoOo#-0*<~Xk5GX<%#Fmpa@Dw_2}_N zGiX!>sOKv~xX+TctGgybLGl@80J_4N`E*#r=NG~zx>0l~rYLmU16h{qagr`&I0__k zej}bulBe8tREbCZhc%0n@{U&YYh)-m!C+;$2+*C=U;s%#fxBF90`zA1OuK7%0VNKJ ztl{FCY}M6A_Es_sfOQcwgHJok?n+seYZsu1a-z!ep8sTa;gpInb9B|bWlr&`p?Gpj z3XWrg_1G5?ZVF63TMxq@!z&tA6~_#WCf0isiNvBjYMI(fBzBYywuV;qZDhd4w!3S$ za(=B5&2BhCx08m7WUfriyut(%?q{;0RPCcQP+iN2kR~CCZqTK}+bL2CIy$2T_P72C Z`Va3>H*!GK)xiJ&002ovPDHLkV1nps+ZX@< literal 0 HcmV?d00001 diff --git a/assets-cg/menu_title.png b/assets-cg/menu_title.png new file mode 100644 index 0000000000000000000000000000000000000000..be04450e4cb5da4b319ae52fdf3fdd0e29db005f GIT binary patch literal 1711 zcmV;g22lBlP)EX>4Tx04R}tkv&MmKpe$iQ$>-gh#f>6GE^rkq9Tr3g(6f4wL+^7CYOFelZGV4 z#ZhoAIQX$xb#QUk)xlK|1V2C=otzY1q{ROvg%&X$9QWhhy~o`h822neAEeHfFOWz0!Z0>0zx9s$1IMR}J0xj#p@nza}Z5Q%4*VcNtS#M7I$ z!FiuJ%nGtfd`>)S(glehxvqHp#yRh@z%#>UDmhOaCKmH8th6vIm>TgEaYWU0$`{fe ztDLtuYo!Wn+>^gBl+{<3xlVHkaV%m95=1DdpokJ|L}}GYv5=(wn2&$Z^-JVZ$W;U* z#{$aGAiI9>Klt6Pm7ARKl0q?{{l#%UhJlV0Yqi@Op{kK5Zn%7%nAEysMin>bN00)P_ zNS?CSecs*K+S|Wp8vXqMQciN3Ffd|300006VoOIv0RI600RN!9r;`8x010qNS#tmY zE+YT{E+YYWr9XB6000McNliru<^l{5G%iO4eSad^gZEa<4bO1wgWnpw> zWFU8GbZ8()Nlj2!fese{00f6gL_t(|+U;FguH+yLY$NV}WzSbMya?j03}lHk(x{UF zc6m4C(GPR+#Xb6cm+B%Q)6sK%V1NPm00A+FH_^t|TxG$W{IR`%RV2fh|G!4`DFDi3 zu$Jk625SIdW(klFEiV`&01gn2`vyQhFd~}K1LVy7_O~8D$zY&DZ+6@d(-t;UXGXUG z2w>bH@|zkt7PJXDYXw;9`DW1>BfwBG+sH6c&!0;3Em8!IPw<7#3 zAS1sf35yh-OY@wBha#F0_g(@a2Qe*C$M5a^2oSyTwd;$_vrRd~B2n$LQ38AO5|KHm z&;W6a*^VCtq@Y320MQcAGV1vK%B)!C)iCM&yA=pz_+F48SVQ05|E-Q+%Zw4wR?~V_ zeb@pFk7>~`TkCVOLTRn}L1{=qEVL(iDEo|h2Ps^dLg_Y|p#>z$REc;LXd^$?{zGVI zogr1dg?bs4ZJJPm;K?%_tmbuP|YCRYl`)jq&60Ql7yNlgzDwfG2$#$GTG@=MWe&2&tKv@Qy;MfOf^~ zl}PXm97v8%;$r_*EvgW&vQyDXhewfv}pn9>Yi7$1TCoRAM!amMD=U zAR_baQhY2P3GXd>rz)zHIU7qD%QZuc8j$Tq6bQ*0;o1tjEVSJv^VZa~02;x@=O_Zk zvJGrkky5LXy=^`dG=hWRh0jQ^mMBHYdAYS-TMdmA77YnrMrnxPW-7~HXnj${;VuN? z7>B0%j9qI{%5`y6I6d^QtlF}cVLKNYN0kMk;=t*ep)fDBLbI|=s?>f>Px?T_-M&XM zI=>hJrtGf%=mKK~5U8VbH7dafDJ zp8-K@#x!SWQTND+Bg@<#+kfue2uI8Q1T__9qKsWTl0sW=CLYZ>kWrj5r=i;y3az%; z!))vE1}~td9(d415lTCvfJA}N^!+_km1Gsv07O|P0ohg5%paYvPST2FT=Mu3kI9+^ zg!i~Y@4Y%%D;NzxmIJVe+B>~ZTX z!h6!0#9Xg4?^OtpiK%KwVM->909a{WXp)Ac|GA27d*GRZ;PfB#vlli^&sTstX70Y* zX1DtcZ7g0Ch3DzF_FLz-tkKkDLbUc)Ot{`~bh*AsQV)~2E3mh4;BWb}4^NJ!=x*A( zt7I`~?R+O*ye7|hqAKs8A+$)#9urNyDqr}Boug%`*FV%&Rszb@w{idg002ovPDHLk FV1hD^74QH6 literal 0 HcmV?d00001 diff --git a/src/main.c b/src/main.c index 72111f1..0509e9b 100644 --- a/src/main.c +++ b/src/main.c @@ -12,6 +12,7 @@ #include "geometry.h" #include "level.h" #include "map.h" +#include "menu.h" #include "pathfinding.h" #include "player.h" #include "render.h" @@ -33,22 +34,6 @@ #include -int main_menu(void) -{ - while(1) { - dclear(C_BLACK); - dprint(1, 1, C_WHITE, "Rogue Life"); - dprint(1, 15, C_WHITE, "[1] Cavern"); - dprint(1, 29, C_WHITE, "[2] Lab"); - dupdate(); - - int key = getkey().key; - if(key == KEY_EXIT) return -1; - if(key == KEY_1) return 1; - if(key == KEY_2) return 2; - } -} - int main(void) { /* Enable %f etc. in printf()-like functions */ @@ -63,12 +48,12 @@ int main(void) usb_interface_t const *interfaces[] = { &usb_ff_bulk, NULL }; usb_open(interfaces, GINT_CALL_NULL); - int lv = main_menu(); + int lv = menu_level_select(0); level_t const *level = NULL; if(lv == -1) return 1; - if(lv == 1) level = &level_lv1; - if(lv == 2) level = &level_lv2; + if(lv == 0) level = &level_lv1; + if(lv == 1) level = &level_lv2; game_t game = { 0 }; camera_t *c = &game.camera; diff --git a/src/menu.c b/src/menu.c new file mode 100644 index 0000000..7f9ba42 --- /dev/null +++ b/src/menu.c @@ -0,0 +1,131 @@ +#include "map.h" +#include "level.h" +#include "render.h" +#include +#include +#include +#include +#include + +typedef struct { + /* Same mechanism as in game_t */ + map_t const *map; + uint16_t *map_anim; + camera_t camera; + +} menu_game_t; + +static menu_game_t *menu_load_game(level_t const *level) +{ + menu_game_t *mg = malloc(sizeof *mg); + mg->map = level->map; + mg->map_anim = malloc(mg->map->width * mg->map->height * + sizeof *mg->map_anim); + + for(int i = 0; i < mg->map->width * mg->map->height; i++) + mg->map_anim[i] = rand() & 4095; + + camera_init(&mg->camera, mg->map); + + return mg; +} + +static void menu_destroy_game(menu_game_t *mg) +{ + free(mg->map_anim); + free(mg); +} + +static void menu_render_game(menu_game_t *mg) +{ + render_map_layer(mg->map, &mg->camera, 0, 0, HORIZONTAL, mg->map_anim, + DIMAGE_NONE); + render_map_layer(mg->map, &mg->camera, 0, 0, VERTICAL, mg->map_anim, + DIMAGE_NONE); + render_map_layer(mg->map, &mg->camera, 0, 0, CEILING, mg->map_anim, + DIMAGE_NONE); +} + +static void menu_update_animations(menu_game_t *mg, fixed_t dt) +{ + for(int i = 0; i < mg->map->width * mg->map->height; i++) + mg->map_anim[i] += fround(dt * 1000); +} + +int menu_level_select(int start) +{ + extern bopti_image_t img_menu_title, img_menu_arrows; + + menu_game_t *options[2]; + #define OPTION_COUNT ((int)(sizeof options / sizeof options[0])) + + options[0] = menu_load_game(&level_lv1); + options[1] = menu_load_game(&level_lv2); + + int selection = (start >= 0 && start < OPTION_COUNT) ? start : 0; + int target_x=0, x=0; + + int volatile frame_tick = 1; + int t = timer_configure(TIMER_ANY, 1000000 / FRAME_RATE, + GINT_CALL_SET(&frame_tick)); + timer_start(t); + + while(1) { + while(!frame_tick) + sleep(); + fixed_t dt = fix(1) / FRAME_RATE; + + if(x != target_x) { + int dx = (target_x - x) / 6; + if(x < target_x && dx < 6) + dx = min(target_x-x, 6); + if(x > target_x && dx > -6) + dx = max(target_x-x, -6); + x += dx; + } + + dclear(C_BLACK); + + for(int i = 0; i < OPTION_COUNT; i++) { + if(-x <= (i-1)*DWIDTH) + continue; + if(-x >= (i+1)*DWIDTH) + continue; + + int local_offset = x + i*DWIDTH; + fixed_t map_center = fix(options[i]->map->width) / 2; + options[i]->camera.x = map_center - fix(local_offset) / TILE_WIDTH; + menu_render_game(options[i]); + } + + dimage(148, 9, &img_menu_title); + if(selection > 0) + dsubimage(16, 93, &img_menu_arrows, 0, 0, 35, 42, DIMAGE_NOCLIP); + if(selection < OPTION_COUNT - 1) + dsubimage(345, 93, &img_menu_arrows, 35, 0, 35, 42, DIMAGE_NOCLIP); + + dupdate(); + + for(int i = 0; i < OPTION_COUNT; i++) + menu_update_animations(options[i], dt); + + int key = getkey_opt(GETKEY_MENU, &frame_tick).key; + if(key == KEY_LEFT && selection > 0) { + selection--; + target_x += DWIDTH; + } + if(key == KEY_RIGHT && selection < OPTION_COUNT - 1) { + selection++; + target_x -= DWIDTH; + } + if(key == KEY_EXE || key == KEY_SHIFT) + return selection; + } + + timer_stop(t); + + for(size_t i = 0; i < OPTION_COUNT; i++) + menu_destroy_game(options[i]); + + return 0; +} diff --git a/src/menu.h b/src/menu.h new file mode 100644 index 0000000..5317351 --- /dev/null +++ b/src/menu.h @@ -0,0 +1,9 @@ +//--- +// menu: Main menu +//--- + +#pragma once + +/* Run the main menu and return the selected entry. start is the entry shown at + the start; default 0. */ +int menu_level_select(int start); diff --git a/src/render.c b/src/render.c index 2544242..5b36e4a 100644 --- a/src/render.c +++ b/src/render.c @@ -123,7 +123,7 @@ fixed_t camera_ppu(camera_t const *c) //--- static inline void render_tile(int x, int y, tileset_t const *tileset, - int tile_id, int time_ms) + int tile_id, int time_ms, int flags) { /* If the tile is animated, find the position in the cycle */ if(tileset->tiles[tile_id].anim_length > 0) { @@ -147,14 +147,12 @@ static inline void render_tile(int x, int y, tileset_t const *tileset, dsubimage(x, y, tileset->sheet, TILE_WIDTH * (tile_id % tileset->width), TILE_HEIGHT * (tile_id / tileset->width), - TILE_WIDTH, TILE_HEIGHT, DIMAGE_NOCLIP); + TILE_WIDTH, TILE_HEIGHT, flags); } -static void render_map_layer(game_t const *game, camera_t const *c, int ss_x, - int ss_y, int layer) +void render_map_layer(map_t const *map, camera_t const *c, int ss_x, int ss_y, + int layer, uint16_t *map_anim, int flags) { - map_t const *map = game->map; - /* Render floor and walls */ for(int row = -2; row < map->height + 2; row++) for(int col = -1; col < map->width + 1; col++) { @@ -170,12 +168,12 @@ static void render_map_layer(game_t const *game, camera_t const *c, int ss_x, continue; } - int time_ms = game->map_anim[map->width * row + col]; + int time_ms = map_anim ? map_anim[map->width * row + col] : 0; if(map->tileset->tiles[cell->base].plane == layer) - render_tile(p.x, p.y, map->tileset, cell->base, time_ms); + render_tile(p.x, p.y, map->tileset, cell->base, time_ms, flags); if(cell->decor && map->tileset->tiles[cell->decor].plane == layer) - render_tile(p.x, p.y, map->tileset, cell->decor, time_ms); + render_tile(p.x, p.y, map->tileset, cell->decor, time_ms, flags); } } @@ -397,16 +395,19 @@ void render_game(game_t const *g, bool show_hitboxes) prof_enter(ctx); /* Render map floor and floor entities */ - render_map_layer(g, camera, ss_x, ss_y, HORIZONTAL); + render_map_layer(g->map, camera, ss_x, ss_y, HORIZONTAL, g->map_anim, + DIMAGE_NOCLIP); render_entities(g, camera, floor_depth_measure, ss_x, ss_y, show_hitboxes); /* Render map walls and vertical entities TODO ECS: Sort walls and wall entities together for proper ordering!*/ - render_map_layer(g, camera, ss_x, ss_y, VERTICAL); + render_map_layer(g->map, camera, ss_x, ss_y, VERTICAL, g->map_anim, + DIMAGE_NOCLIP); render_entities(g, camera, wall_depth_measure, ss_x, ss_y, show_hitboxes); /* Render ceiling tiles (including out of bounds) and ceiling entities */ - render_map_layer(g, camera, ss_x, ss_y, CEILING); + render_map_layer(g->map, camera, ss_x, ss_y, CEILING, g->map_anim, + DIMAGE_NOCLIP); render_entities(g, camera, ceiling_depth_measure, ss_x,ss_y,show_hitboxes); prof_leave(ctx); diff --git a/src/render.h b/src/render.h index 20d9faa..a7847a4 100644 --- a/src/render.h +++ b/src/render.h @@ -59,6 +59,14 @@ fixed_t camera_ppu(camera_t const *c); /* Render window overlay. */ void render_window(int x, int y, int w, int h); +/* Render a single layer of the map, with animated tiles. + ss_x and ss_y are additional displacement for screenshake; default 0. + layer is HORIZONTAL (floor), VERTICAL (wall), or CEILING. + map_anim is the indivual tiles' animation time, can be NULL. + flags is the flags for dsubimage() for the tiles. */ +void render_map_layer(map_t const *map, camera_t const *c, int ss_x, int ss_y, + int layer, uint16_t *map_anim, int flags); + /* Render game full-screen. */ struct game; void render_game(struct game const *g, bool show_hitboxes);