Add a proper credits screen and a heads-up display

This commit is contained in:
Lucas S. Vieira 2024-11-01 18:21:59 -03:00
parent 09d5493087
commit 48e6d2bded
11 changed files with 378 additions and 14 deletions

View file

@ -16,6 +16,9 @@ void font_flush();
void font_draw_big(const char *text, int16_t vx, int16_t vy);
void font_draw_sm(const char *text, int16_t vx, int16_t vy);
uint16_t font_measurew_big(const char *text);
uint16_t font_measurew_sm(const char *text);
void font_set_color(uint8_t r0, uint8_t g0, uint8_t b0);
void font_reset_color();

View file

@ -11,6 +11,7 @@ typedef enum {
SCREEN_TITLE,
SCREEN_MODELTEST,
SCREEN_COMINGSOON,
SCREEN_CREDITS,
} ScreenIndex;
void scene_change(ScreenIndex scr);

View file

@ -0,0 +1,9 @@
#ifndef SCREENS_CREDITS_H
#define SCREENS_CREDITS_H
void screen_credits_load();
void screen_credits_unload(void *);
void screen_credits_update(void *);
void screen_credits_draw(void *);
#endif

View file

@ -152,6 +152,84 @@ _draw_glyph(
sort_prim(sprt, 1); // 1 = HUD and text layer
}
uint16_t
_font_measurew_generic(const char *text,
const uint8_t ws_w,
const uint8_t gap,
uint8_t *ginfo)
{
uint16_t w = 0;
uint16_t vx = 0;
while(*text != '\0') {
switch(*text) {
case ' ':
vx += ws_w + gap;
text++;
continue;
case '\n':
vx = 0;
text++;
continue;
case '\t':
vx += (ws_w << 2) + (gap << 2);
text++;
continue;
}
uint8_t offset = 0xff;
if((*text >= 'a') && (*text <= 'z'))
offset = (uint8_t)((unsigned)(*text) - (unsigned)('a'));
else if((*text >= 'A') && (*text <= 'Z'))
offset = (uint8_t)((unsigned)(*text) - (unsigned)('A'));
else if((*text >= '0') && (*text <= '9')) {
offset = 26 + (uint8_t)((unsigned)(*text) - (unsigned)('0'));
} else {
switch(*text) {
case '*': offset = 36; break;
case '.': offset = 37; break;
case ';': offset = 38; break;
case '-': offset = 39; break;
case '=': offset = 40; break;
case '!': offset = 41; break;
case '?': offset = 42; break;
default: offset = 0xff; break;
}
}
uint8_t gw = ws_w;
if(offset != 0xff) {
uint8_t *info = &ginfo[offset * 4];
if(info[0] == 0xff) {
// Glyph doesn't exist, so don't draw
goto jump_ws;
}
gw = info[2];
}
jump_ws:
vx += gw + gap;
text++;
if(vx > w) w = vx;
}
return w;
}
uint16_t
font_measurew_big(const char *text)
{
return _font_measurew_generic(
text, GLYPH_WHITE_WIDTH, GLYPH_GAP, glyph_info_big);
}
uint16_t
font_measurew_sm(const char *text)
{
return _font_measurew_generic(
text, GLYPH_SML_WHITE_WIDTH, GLYPH_SML_GAP, glyph_info_sm);
}
void
_font_draw_generic(const char *text, int16_t vx, int16_t vy,
const uint8_t ws_w, const uint8_t ws_h,

View file

@ -25,6 +25,9 @@ extern SoundEffect sfx_sprn;
extern SoundEffect sfx_chek;
extern int debug_mode;
extern uint8_t level_ring_count;
extern uint8_t level_score_count;
// Update functions
static void _ring_update(ObjectState *state, ObjectTableEntry *typedata, VECTOR *pos);
static void _goal_sign_update(ObjectState *state, ObjectTableEntry *typedata, VECTOR *pos);
@ -112,6 +115,7 @@ _ring_update(ObjectState *state, ObjectTableEntry *, VECTOR *pos)
state->anim_state.animation = 1;
state->anim_state.frame = 0;
state->props ^= OBJ_FLAG_ANIM_LOCK; // Unlock from global timer
level_ring_count++;
sound_play_vag(sfx_ring, 0);
}
} else if(state->anim_state.animation == OBJ_ANIMATION_NO_ANIMATION
@ -183,6 +187,14 @@ _monitor_update(ObjectState *state, ObjectTableEntry *, VECTOR *pos)
state->frag_anim_state->animation = OBJ_ANIMATION_NO_ANIMATION;
sound_play_vag(sfx_pop, 0);
switch(((MonitorExtra *)state->extra)->kind) {
case MONITOR_KIND_RING:
level_ring_count += 10;
sound_play_vag(sfx_ring, 0);
break;
default: break;
}
if(!player.grnd && player.vel.vy > 0) {
player.vel.vy *= -1;
}

View file

@ -16,6 +16,7 @@
#include "screens/title.h"
#include "screens/modeltest.h"
#include "screens/comingsoon.h"
#include "screens/credits.h"
// Start with 64k until we make the actual level scene
// an object as well
@ -57,8 +58,6 @@ void
scene_change(ScreenIndex scr)
{
printf("Change scene: %d -> %d\n", current_scene, scr);
//set_clear_color(0, 0, 0);
//render_loading_text();
render_loading_logo();
if(current_scene >= 0)
@ -78,6 +77,7 @@ scene_load()
case SCREEN_TITLE: screen_title_load(); break;
case SCREEN_MODELTEST: screen_modeltest_load(); break;
case SCREEN_COMINGSOON: screen_comingsoon_load(); break;
case SCREEN_CREDITS: screen_credits_load(); break;
default: break; // Unknown scene???
}
}
@ -93,6 +93,7 @@ scene_unload()
case SCREEN_TITLE: screen_title_unload(scene_data); break;
case SCREEN_MODELTEST: screen_modeltest_unload(scene_data); break;
case SCREEN_COMINGSOON: screen_comingsoon_unload(scene_data); break;
case SCREEN_CREDITS: screen_credits_unload(scene_data); break;
default: break; // Unknown scene???
}
}
@ -108,6 +109,7 @@ scene_update()
case SCREEN_TITLE: screen_title_update(scene_data); break;
case SCREEN_MODELTEST: screen_modeltest_update(scene_data); break;
case SCREEN_COMINGSOON: screen_comingsoon_update(scene_data); break;
case SCREEN_CREDITS: screen_credits_update(scene_data); break;
default: break; // Unknown scene???
}
}
@ -124,6 +126,7 @@ scene_draw()
case SCREEN_TITLE: screen_title_draw(scene_data); break;
case SCREEN_MODELTEST: screen_modeltest_draw(scene_data); break;
case SCREEN_COMINGSOON: screen_comingsoon_draw(scene_data); break;
case SCREEN_CREDITS: screen_credits_draw(scene_data); break;
default: break; // Unknown scene???
}
}

View file

@ -7,8 +7,6 @@
#include "input.h"
#include "sound.h"
#include "screens/fmv.h"
typedef struct {
int32_t prectx;
int32_t precty;
@ -42,6 +40,7 @@ screen_comingsoon_load()
void
screen_comingsoon_unload(void *)
{
sound_stop_xa();
screen_free();
}
@ -68,11 +67,7 @@ screen_comingsoon_update(void *d)
}
} else {
if(data->fade > 0) data->fade -= 2;
else {
screen_fmv_set_next(SCREEN_TITLE);
screen_fmv_enqueue("\\SONICT.STR;1");
scene_change(SCREEN_FMV);
}
else scene_change(SCREEN_CREDITS);
}
}

237
src/screen_credits.c Normal file
View file

@ -0,0 +1,237 @@
#include <stdlib.h>
#include <stdint.h>
#include "screen.h"
#include "screens/credits.h"
#include "render.h"
#include "basic_font.h"
#include "input.h"
static const char *creditstxt[] = {
"SONIC XA CREDITS",
"\r",
/* Programming credits */
"Lead Developer",
"luksamuk",
"\r",
"Level Design",
"luksamuk",
"\r",
/* BGM */
"background music",
"\r",
"Amen Break Remixed Loop 01 160 BPM",
"By u_ul6ysm8501",
"\r",
"Playgrond Zone 1",
"Rusty Ruin Act 1",
"By Richard Jacques",
"\r",
"Playground Zone 2",
"Rusty Ruin Act 2",
"By Richard Jacques",
"\r",
"Playground Zone 3",
"Let Mom Sleep",
"By Hideki Naganuma",
"\r",
"Playground Zone 4",
"Let Mom Sleep Remix",
"By Hideki Naganuma",
"Remixed by Richard Jacques",
"\r",
"Green Hill Zone",
"Palmtree Panic P Mix",
"By Sonic Team",
"\r",
"Surely Wood Zone",
"El Gato Battle 2 Vortex Remake",
"By pkVortex",
"\r",
"You Can Do Anything",
"By Sonic Team",
"\r",
/* Graphics credits */
"Character Sprites Ripping",
"Triangly",
"Paraemon",
"\r",
"Level and Menu Graphics Ripping",
"Paraemon",
"WarCR",
"\r",
"Original Level Graphics",
"NiteShadow",
"Techokami",
"\r",
"Fonts Ripping",
"Flare",
"Storice",
"\r",
"Original Fonts",
"Mr. Alien",
"\r",
"Objects Ripping",
"Paraemon",
"\r",
"Original Sprites and Tiles",
"Sonic Team",
"\r",
/* Ending */
"sonic the hedgehog owned by",
"sonic team",
"sega inc",
"\r",
"special thanks",
"PSXDEV Network",
"The Spriters Resource",
"\r",
NULL,
};
#define SLIDE_FRAMES 128
#define FADE_FRAMES 64
typedef struct {
uint8_t entry;
uint8_t fade;
int8_t countdown;
uint8_t state;
} screen_credits_data;
void
screen_credits_load()
{
screen_credits_data *data = screen_alloc(sizeof(screen_credits_data));
set_clear_color(0, 0, 0);
data->entry = 0;
data->fade = 0;
data->countdown = FADE_FRAMES;
data->state = 0;
}
void
screen_credits_unload(void *)
{
screen_free();
}
void
screen_credits_update(void *d)
{
screen_credits_data *data = (screen_credits_data *)d;
if(creditstxt[data->entry] != NULL
&& (!pad_pressed(PAD_START) && !pad_pressed(PAD_CROSS))) {
switch(data->state) {
case 0: // Pre-fade-in
data->countdown = FADE_FRAMES;
data->fade = 0;
data->state = 1;
break;
case 1: // Fade-in
data->countdown--;
data->fade += 2;
if(data->countdown == 0) {
data->state = 2;
data->countdown = SLIDE_FRAMES;
}
break;
case 2: // Displaying
data->countdown--;
if(data->countdown == 0) {
data->countdown = FADE_FRAMES;
data->state = 3;
}
break;
case 3: // Fade-out
data->countdown--;
data->fade -= 2;
if(data->countdown == 0) {
while(creditstxt[data->entry] != NULL
&& creditstxt[data->entry][0] != '\r')
data->entry++;
if(creditstxt[data->entry] != NULL &&
creditstxt[data->entry][0] == '\r') {
data->entry++;
data->state = 0;
}
}
break;
default: break;
}
} else {
// Go to title screen
scene_change(SCREEN_TITLE);
}
}
void
screen_credits_draw(void *d)
{
screen_credits_data *data = (screen_credits_data *)d;
// Get current text
const char **window = &creditstxt[data->entry];
if(*window != NULL) {
// Calculate height and vy
const char **cursor = window;
uint16_t height = 0;
while((*cursor)[0] != '\r') {
cursor++;
height += GLYPH_WHITE_HEIGHT + GLYPH_GAP;
}
int16_t vy = CENTERY - (height >> 1);
// Render text line by line
cursor = window;
uint8_t is_first = 1;
while((*cursor)[0] != '\r') {
uint16_t width = font_measurew_big(*cursor);
int16_t vx = CENTERX - (width >> 1);
if(is_first) {
font_set_color(
LERPC(data->fade, 128),
LERPC(data->fade, 128),
LERPC(data->fade, 0));
} else {
font_set_color(
LERPC(data->fade, 128),
LERPC(data->fade, 128),
LERPC(data->fade, 128));
}
font_draw_big(*cursor, vx, vy);
if(is_first) is_first = 0;
cursor++;
vy += GLYPH_WHITE_HEIGHT + GLYPH_GAP;
}
}
}

View file

@ -17,6 +17,7 @@
#include "model.h"
#include "object.h"
#include "parallax.h"
#include "basic_font.h"
extern int debug_mode;
@ -32,6 +33,8 @@ LevelData leveldata;
Camera camera;
ObjectTable obj_table_common;
uint8_t level_fade;
uint8_t level_ring_count;
uint8_t level_score_count;
#define CHANNELS_PER_BGM 3
static uint32_t bgm_loop_sectors[] = {
@ -110,6 +113,9 @@ screen_level_load()
reset_elapsed_frames();
level_fade = 0;
level_ring_count = 0;
level_score_count = 0;
}
void
@ -282,6 +288,24 @@ screen_level_draw(void *d)
draw_text(x, CENTERY - 12, 0, line1);
}
// Heads-up display
font_set_color(
LERPC(level_fade, 0x7f),
LERPC(level_fade, 0x7f),
0);
font_draw_big("SCORE", 10, 10);
font_draw_big("TIME", 10, 24);
font_draw_big("RINGS", 10, 38);
font_set_color(
LERPC(level_fade, 0x7f),
LERPC(level_fade, 0x7f),
LERPC(level_fade, 0x7f));
font_draw_big("00000000", 60, 10);
font_draw_big("0:00", 54, 24);
snprintf(buffer, 255, "%3d", level_ring_count);
font_draw_big(buffer, 60, 38);
font_reset_color();
if(debug_mode) {
// Video debug
snprintf(buffer, 255,

View file

@ -19,7 +19,8 @@
#define CHOICE_SONICT 9
#define CHOICE_INTRO 10
#define CHOICE_SOON 11
#define MAX_LEVELS (CHOICE_SOON + 1)
#define CHOICE_CREDITS 12
#define MAX_LEVELS (CHOICE_CREDITS + 1)
typedef struct {
uint8_t menu_choice;
@ -37,8 +38,8 @@ extern int debug_mode;
static const char *menutext[] = {
"PLAYGROUND ZONE 1",
" ZONE 2",
"TESTBED ZONE 1",
" ZONE 2",
" ZONE 3",
" ZONE 4",
"GREEN HILL ZONE 1",
" ZONE 2",
"SURELY WOOD ZONE 1",
@ -46,13 +47,13 @@ static const char *menutext[] = {
"\n",
"\n",
"\n",
"\n",
"MODELTEST",
"TITLESCREEN",
"SONICTEAM",
"INTRO",
"COMINGSOON",
"CREDITS",
NULL,
};
@ -154,6 +155,8 @@ screen_levelselect_update(void *d)
scene_change(SCREEN_MODELTEST);
} else if(data->menu_choice == CHOICE_SOON) {
scene_change(SCREEN_COMINGSOON);
} else if(data->menu_choice == CHOICE_CREDITS) {
scene_change(SCREEN_CREDITS);
} else {
screen_level_setlevel(data->menu_choice);
scene_change(SCREEN_LEVEL);

View file

@ -9,7 +9,6 @@
#include "input.h"
#include "screen.h"
#include "sound.h"
/* #include "model.h" */
#include "timer.h"
#include "screens/fmv.h"