From a89a40342c3bfd3e40072d41e27622473bb4f9e1 Mon Sep 17 00:00:00 2001 From: "Lucas S. Vieira" Date: Thu, 10 Apr 2025 23:37:22 -0300 Subject: [PATCH] Add character select --- assets/misc/CHARSEL.TIM | Bin 0 -> 16020 bytes assets/misc/charsel.png | Bin 0 -> 2790 bytes include/screen.h | 1 + include/screens/charselect.h | 9 ++ iso.xml | 3 + src/screen.c | 5 + src/screen_charselect.c | 183 +++++++++++++++++++++++++++++++++++ src/screen_title.c | 4 +- 8 files changed, 202 insertions(+), 3 deletions(-) create mode 100644 assets/misc/CHARSEL.TIM create mode 100644 assets/misc/charsel.png create mode 100644 include/screens/charselect.h create mode 100644 src/screen_charselect.c diff --git a/assets/misc/CHARSEL.TIM b/assets/misc/CHARSEL.TIM new file mode 100644 index 0000000000000000000000000000000000000000..ec500445be287760a93bad9f292393c63149d050 GIT binary patch literal 16020 zcmWe&U|?WkU|{I6V_;}tWMDYKpusQ-hEfPL@xcqu6QDQ%#RUw5_|gKyJx&^O zK(PTSum0B~Y7tN^Pg&g#Dy8C?K(Rq?d_ZF2e?5eruQJ?Y1XOcFFeq<;SRfiDhme&& zKrsQr@k~K^L3yAUiD#Nbv`N@=2P960Z;XIKABG`$;ac6bI!HbN@j>Mkh);AdLKQ;d z0aRmvur(tnHiGi38OezcMC}2pPeAbm$s@zB%!0)Pga(B_DBS+ngJJ;017VN|F(nox z2JSO~!h8|~sK&U@1cRV@0~9A9IcS6tpg^Gx$pr|KR{)e&g7Oe@!z@O??J7`<77-ty zxX|;&abY@r8>574*>hz%*PKqCmqF|u0~;WAJ;hRvj*B?j`vwK@<9i4RCzfZ_n+ zZx9RNXTp91=ptu0l!l3*CD!)MS0*Mh&egWw~#h}`oR{#_X zAQq?wkrn{C4dfme7GglSOw$uovRgq=1_lrtQnQ2HhQJ_km~K#tf|P#P%RiX9!B2xq z3{VaMmFJKcVK71X8N?#p5777p_hLYyB*XxU4^T{KdO~{cpxlE!XFzNN^(#Q-6o>}J z2qcGqVt`iw($+G~hE86c4cYfY6{A0kJ@R!)tXQ7Nl+gnF6v8WEUhqf>Ibr zbm(G`e?VmusLl{#U@!sEkh&9;KR~fUs^38M11Ls77!n_vo}l&?q*q9sX`mbc${UDU z1QY|HG7gqQAfqU}0-%}+lrKQ7Ur@Z@%X!4vJ7-4Z|A4$=+6ppt36 z3Mh<0r4%SHfJ_3}0+9v938?-9#WyIQgW?5OFB7C=h+z{@d^F^MVgwWqFdq`@3s5+L zQm>{bq+|kx5~%I~g(0ZV4(h!VXBH?PKxrQo-k@8>1mkw!5R^fh5dp;pD1C!`NldFAR9}PCgTm@s9VqNUxdGDV0ma7u zdXN|~@d1hnNZ5f`pcsL;0aO-2@c(*H-2zcFY)MdDFo5e222iO5iw{r;;LjhR(7IL! z8b1W(2T<(|sWl)iTSTfRHa?I;7!)I*+yb%#6x$%zfMOgJOUUYmE!zY7)%suLH-A|ukiQWKsf@0uhoIVABI7B1r$G^90ID@L2&^p&p;&@$P&D`!GXzv z36d{B;SL%r0kwHSA|N+udV*LZCVzlSEYQ3;g9#`l;*SwfOn_8@N)cFGfM#hS@c|-1 zGgP2h0M+j>HlEmMU;xL40}~`qfZ_ra^2m8%WW@$(ToJUA0aTBGY65)m0crPx(k!fw z0L2L?7C?CdM1#sPP;5YA0931h+F&@#zXnj>6O=pdBjpf?UN{MkFOWQ3WEe9*=^Z>< zZUPEPP_6L<2(uNC#*{3u}xtfW|Q)=HYB%LrXM}>xTY(2Z1!y0P-a$4nXA(C{=^v1z&uC z5e*Ct zyH!DIAogN01xfJ`XM)2^hymnNP^ig+X5es+izC7W6uO`g1jPoZjsWQgiCwD$g+BRbLjEGVI>5f-GCsX~%r0L{3=VgqC*7Q3Nt1m&Z6CWtFR zvnyCsq8L0RIZ(fX`dpxJgrqzWiM6a}U;vNZg6eWm*rVnRkO+9r2~<|02Sx*^Mgge+ zg*jSz(*TPPkiA$!8R|w*J%fl3kXl@BM7Mkh@j+pS3*+zxsNM#}AE+Jyl~j{JwLiiP Y9J(0rsY2+6uu#o_h+!vDRStR{0K(-$PXGV_ literal 0 HcmV?d00001 diff --git a/assets/misc/charsel.png b/assets/misc/charsel.png new file mode 100644 index 0000000000000000000000000000000000000000..c109dcc7155c6f1330cd57d73a7c281869c72a7b GIT binary patch literal 2790 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 + diff --git a/src/screen.c b/src/screen.c index 3c21aaf..dd61f41 100644 --- a/src/screen.c +++ b/src/screen.c @@ -17,6 +17,7 @@ #include "screens/slide.h" #include "screens/credits.h" #include "screens/sprite_test.h" +#include "screens/charselect.h" // Start with 64k until we make the actual level scene // an object as well @@ -80,6 +81,7 @@ scene_load() case SCREEN_SLIDE: screen_slide_load(); break; case SCREEN_CREDITS: screen_credits_load(); break; case SCREEN_SPRITETEST: screen_sprite_test_load(); break; + case SCREEN_CHARSELECT: screen_charselect_load(); break; default: break; // Unknown scene??? } } @@ -96,6 +98,7 @@ scene_unload() case SCREEN_SLIDE: screen_slide_unload(scene_data); break; case SCREEN_CREDITS: screen_credits_unload(scene_data); break; case SCREEN_SPRITETEST: screen_sprite_test_unload(scene_data); break; + case SCREEN_CHARSELECT: screen_charselect_unload(scene_data); break; default: break; // Unknown scene??? } } @@ -112,6 +115,7 @@ scene_update() case SCREEN_SLIDE: screen_slide_update(scene_data); break; case SCREEN_CREDITS: screen_credits_update(scene_data); break; case SCREEN_SPRITETEST: screen_sprite_test_update(scene_data); break; + case SCREEN_CHARSELECT: screen_charselect_update(scene_data); break; default: break; // Unknown scene??? } } @@ -129,6 +133,7 @@ scene_draw() case SCREEN_SLIDE: screen_slide_draw(scene_data); break; case SCREEN_CREDITS: screen_credits_draw(scene_data); break; case SCREEN_SPRITETEST: screen_sprite_test_draw(scene_data); break; + case SCREEN_CHARSELECT: screen_charselect_draw(scene_data); break; default: break; // Unknown scene??? } } diff --git a/src/screen_charselect.c b/src/screen_charselect.c new file mode 100644 index 0000000..036e714 --- /dev/null +++ b/src/screen_charselect.c @@ -0,0 +1,183 @@ +#include +#include "screen.h" +#include "screens/charselect.h" +#include "player.h" +#include "render.h" +#include "util.h" +#include "input.h" +#include "basic_font.h" +#include "sound.h" + +#include "screens/level.h" + +#define BG_PAUSE 90 +#define BG_FPS 4 +#define BG_FRAMES 4 + +#define CHARSEL_PADDING (SCREEN_XRES >> 2) + +typedef struct { + int8_t character; + int32_t bg_prect_x; + int32_t bg_prect_y; + uint8_t bg_mode; + uint8_t bg_frame; + uint8_t bg_state; + uint16_t bg_timer; + uint8_t charsel_mode; +} screen_charselect_data; + +// TIM 15bpp -- TPAGE: 384x0 +// Sonic: 0x0+22+40 +// Tails: 22x0+40+40 +// Knuckles: 62x0+40+40 + + +void +screen_charselect_load() +{ + screen_charselect_data *data = screen_alloc(sizeof(screen_charselect_data)); + data->character = screen_level_getcharacter(); + + uint32_t length; + TIM_IMAGE tim; + uint8_t *img = file_read("\\MISC\\LVLSEL.TIM;1", &length); + load_texture(img, &tim); + data->bg_mode = tim.mode; + data->bg_prect_x = tim.prect->x; + data->bg_prect_y = tim.prect->y; + free(img); + + img = file_read("\\MISC\\CHARSEL.TIM;1", &length); + load_texture(img, &tim); + data->charsel_mode = tim.mode; + free(img); + + data->bg_frame = 0; + data->bg_state = 0; + data->bg_timer = BG_PAUSE; + + sound_bgm_play(BGM_TITLESCREEN); +} + +void +screen_charselect_unload(void *) +{ + sound_stop_xa(); + screen_free(); +} + +void +screen_charselect_update(void *d) +{ + screen_charselect_data *data = (screen_charselect_data *)d; + + if((data->bg_state == 0) || (data->bg_state == 2)) { + data->bg_timer--; + if(data->bg_timer == 0) { + data->bg_state++; + data->bg_timer = BG_FPS; + } + } else if(data->bg_state == 1) { + data->bg_timer--; + if(data->bg_timer == 0) { + data->bg_frame++; + data->bg_timer = BG_FPS; + if(data->bg_frame == BG_FRAMES - 1) { + data->bg_state++; + data->bg_timer = BG_PAUSE; + } + } + } else if(data->bg_state == 3) { + data->bg_timer--; + if(data->bg_timer == 0) { + data->bg_frame--; + data->bg_timer = BG_FPS; + if(data->bg_frame == 0) { + data->bg_state = 0; + data->bg_timer = BG_PAUSE; + } + } + } + + if(pad_pressed(PAD_RIGHT)) data->character++; + if(pad_pressed(PAD_LEFT)) data->character--; + data->character = + (data->character < 0) + ? 0 + : ((data->character > CHARA_MAX) + ? CHARA_MAX + : data->character); + + if(pad_pressed(PAD_CROSS) || pad_pressed(PAD_START)) { + screen_level_setcharacter(data->character); + screen_level_setlevel(6); + screen_level_setmode(LEVEL_MODE_NORMAL); + scene_change(SCREEN_LEVEL); + } +} + +const char * +_get_char_name(int8_t character) +{ + switch(character) { + case CHARA_SONIC: return "SONIC"; + case CHARA_MILES: return "TAILS"; + case CHARA_KNUCKLES: return "KNUCKLES"; + } + return "UNKNOWN"; +} + +void +screen_charselect_draw(void *d) +{ + screen_charselect_data *data = (screen_charselect_data *)d; + + static const char *title = "CHARACTER SELECT"; + + font_set_color(128, 128, 128); + uint16_t text_hsize = font_measurew_big(title) >> 1; + uint16_t text_xpos = CENTERX - text_hsize; + font_draw_big(title, text_xpos, SCREEN_YRES >> 3); + + // Draw characters + for(uint16_t i = 0; i < 3; i++) { + int16_t xpos = (CHARSEL_PADDING + (i * CHARSEL_PADDING)); + int16_t ypos = (SCREEN_YRES >> 1); + uint8_t is_current_char = (data->character == i); + uint8_t dim = is_current_char ? 128 : 64; + POLY_FT4 *poly = (POLY_FT4 *)get_next_prim(); + increment_prim(sizeof(POLY_FT4)); + setPolyFT4(poly); + setRGB0(poly, dim, dim, dim); + setTPage(poly, data->charsel_mode & 0x3, 0, 384, 0); + poly->clut = 0; + setXYWH(poly, xpos - 20, ypos - 20, 40, 40); + setUVWH(poly, 40 * i, 0, 40, 40); + sort_prim(poly, OTZ_LAYER_PLAYER); + + if(is_current_char) { + const char *charname = _get_char_name(data->character); + text_hsize = font_measurew_sm(charname) >> 1; + font_draw_sm(charname, xpos - text_hsize, ypos + 30); + } + } + + // Draw background + for(uint16_t y = 0; y < SCREEN_YRES; y += 32) { + for(uint16_t x = 0; x < SCREEN_XRES; x += 48) { + POLY_FT4 *poly = (POLY_FT4 *)get_next_prim(); + increment_prim(sizeof(POLY_FT4)); + setPolyFT4(poly); + setRGB0(poly, 96, 96, 96); + poly->tpage = getTPage(data->bg_mode & 0x3, + 0, + data->bg_prect_x, + data->bg_prect_y); + poly->clut = 0; + setXYWH(poly, x, y, 48, 32); + setUVWH(poly, 0, 32 * data->bg_frame, 48, 32); + sort_prim(poly, OTZ_LAYER_LEVEL_BG); + } + } +} diff --git a/src/screen_title.c b/src/screen_title.c index d680610..fd01a6e 100644 --- a/src/screen_title.c +++ b/src/screen_title.c @@ -207,9 +207,7 @@ screen_title_update(void *d) case 2: // New Game // Use Surely Wood Zone 1 as first level screen_title_reset_demo(); - screen_level_setlevel(6); - screen_level_setmode(LEVEL_MODE_NORMAL); - data->next_scene = SCREEN_LEVEL; + data->next_scene = SCREEN_CHARSELECT; level_score_count = 0; break; case 3: // Level Select