Refactored and improved menus and transitions

This commit is contained in:
Andrei "Akari" Alexeyev 2017-02-24 23:58:27 +02:00
parent fb50b87ffe
commit 57053210dd
28 changed files with 369 additions and 253 deletions

View file

@ -15,10 +15,10 @@
void handle_events(EventHandler handler, EventFlags flags, void *arg) {
SDL_Event event;
int kbd = flags & EF_Keyboard;
int text = flags & EF_Text;
int menu = flags & EF_Menu;
int game = flags & EF_Game;
bool kbd = flags & EF_Keyboard;
bool text = flags & EF_Text;
bool menu = flags & EF_Menu;
bool game = flags & EF_Game;
// TODO: rewrite text input handling to properly support multibyte characters and IMEs
@ -35,6 +35,7 @@ void handle_events(EventHandler handler, EventFlags flags, void *arg) {
while(SDL_PollEvent(&event)) {
SDL_Scancode scan = event.key.keysym.scancode;
SDL_Keymod mod = event.key.keysym.mod;
bool repeat = event.key.repeat;
switch(event.type) {
case SDL_KEYDOWN:
@ -45,14 +46,13 @@ void handle_events(EventHandler handler, EventFlags flags, void *arg) {
handler(E_SubmitText, 0, arg);
else if(scan == SDL_SCANCODE_BACKSPACE)
handler(E_CharErased, 0, arg);
} else {
} else if(!repeat) {
if(scan == config_get_int(CONFIG_KEY_SCREENSHOT)) {
take_screenshot();
break;
}
if((scan == SDL_SCANCODE_RETURN && (mod & KMOD_ALT)) || scan == config_get_int(CONFIG_KEY_FULLSCREEN)) {
// video_toggle_fullscreen();
config_set_int(CONFIG_FULLSCREEN, !config_get_int(CONFIG_FULLSCREEN));
break;
}
@ -62,7 +62,7 @@ void handle_events(EventHandler handler, EventFlags flags, void *arg) {
handler(E_KeyDown, scan, arg);
}
if(menu) {
if(menu && (!repeat || transition.state == TRANS_IDLE)) {
if(scan == config_get_int(CONFIG_KEY_DOWN) || scan == SDL_SCANCODE_DOWN) {
handler(E_CursorDown, 0, arg);
} else if(scan == config_get_int(CONFIG_KEY_UP) || scan == SDL_SCANCODE_UP) {
@ -78,7 +78,7 @@ void handle_events(EventHandler handler, EventFlags flags, void *arg) {
}
}
if(game && !event.key.repeat) {
if(game && !repeat) {
if(scan == config_get_int(CONFIG_KEY_PAUSE) || scan == SDL_SCANCODE_ESCAPE) {
handler(E_Pause, 0, arg);
} else {
@ -95,7 +95,7 @@ void handle_events(EventHandler handler, EventFlags flags, void *arg) {
handler(E_KeyUp, scan, arg);
}
if(game && !event.key.repeat) {
if(game && !repeat) {
int key = config_key_from_scancode(scan);
if(key >= 0)
handler(E_PlrKeyUp, key, arg);

View file

@ -199,7 +199,6 @@ int main(int argc, char **argv) {
global.diff = atoi(argv[2]);
}
printf("** Entering %s.\n", stg->title);
do {
@ -215,11 +214,7 @@ int main(int argc, char **argv) {
MenuData menu;
create_main_menu(&menu);
printf("-- menu\n");
set_sfx_volume(config_get_float(CONFIG_SFX_VOLUME));
set_bgm_volume(config_get_float(CONFIG_BGM_VOLUME));
start_bgm("bgm_menu");
main_menu_loop(&menu);
menu_loop(&menu);
return 0;
}

View file

@ -20,20 +20,28 @@ void set_shotmode(MenuData *m, void *p) {
void create_shottype_menu(MenuData *m) {
create_menu(m);
m->transition = NULL;
add_menu_entry(m, "Laser Sign|Mirror Sign", set_shotmode, (void *) YoumuOpposite);
add_menu_entry(m, "Star Sign|Haunting Sign", set_shotmode, (void *) YoumuHoming);
}
void char_menu_input(MenuData*);
void draw_char_menu(MenuData*);
void free_char_menu(MenuData*);
void create_char_menu(MenuData *m) {
create_menu(m);
m->input = char_menu_input;
m->draw = draw_char_menu;
m->end = free_char_menu;
m->transition = TransMenuDark;
m->flags = MF_Abortable | MF_Transient;
m->context = malloc(sizeof(MenuData));
create_shottype_menu(m->context);
add_menu_entry(m, "dialog/marisa|Kirisame Marisa|Black Magician", set_player, (void *)Marisa);
add_menu_entry(m, "dialog/youmu|Konpaku Yōmu|Half-Phantom Girl", set_player, (void *)Youmu);
add_menu_entry(m, "dialog/marisa|Kirisame Marisa|Black Magician", set_player, (void *)Marisa)->transition = TransFadeBlack;
add_menu_entry(m, "dialog/youmu|Konpaku Yōmu|Half-Phantom Girl", set_player, (void *)Youmu)->transition = TransFadeBlack;
}
void draw_char_menu(MenuData *menu) {
@ -168,7 +176,3 @@ void free_char_menu(MenuData *menu) {
destroy_menu(mod);
free(mod);
}
int char_menu_loop(MenuData *menu) {
return menu_loop(menu, char_menu_input, draw_char_menu, free_char_menu);
}

View file

@ -12,7 +12,5 @@
void create_char_menu(MenuData *m);
void draw_char_menu(MenuData *menu);
int char_menu_loop(MenuData *menu);
#endif

View file

@ -26,13 +26,13 @@ troll:
if(stagediff == D_Any) {
create_difficulty_menu(&m);
if(difficulty_menu_loop(&m) == -1) {
if(menu_loop(&m) == -1) {
return;
}
}
create_char_menu(&m);
if(char_menu_loop(&m) == -1) {
if(menu_loop(&m) == -1) {
if(stagediff != D_Any) {
return;
}
@ -80,7 +80,7 @@ troll2:
case 2: {
MenuData m;
create_saverpy_menu(&m);
saverpy_menu_loop(&m);
menu_loop(&m);
break;
}
}
@ -162,5 +162,5 @@ void animate_menu_list(MenuData *m) {
}
void menu_commonaction_close(MenuData *menu, void *arg) {
kill_menu(menu);
menu->state = MS_Dead;
}

View file

@ -16,7 +16,8 @@ void set_difficulty(MenuData *m, void *d) {
void create_difficulty_menu(MenuData *m) {
create_menu(m);
m->draw = draw_difficulty_menu;
m->transition = TransMenuDark;
m->flags = MF_Transient | MF_Abortable;
add_menu_entry(m, "Easy\nfor fearful fairies", set_difficulty, (void *)D_Easy);
@ -53,7 +54,3 @@ void draw_difficulty_menu(MenuData *menu) {
glPopMatrix();
}
}
int difficulty_menu_loop(MenuData *menu) {
return menu_loop(menu, NULL, draw_difficulty_menu, NULL);
}

View file

@ -12,6 +12,5 @@
void create_difficulty_menu(MenuData *menu);
void draw_difficulty_menu(MenuData *m);
int difficulty_menu_loop(MenuData *m);
#endif

View file

@ -8,6 +8,7 @@
#include "menu.h"
#include "gameovermenu.h"
#include "ingamemenu.h"
#include "global.h"
void continue_game(MenuData *m, void *arg)
@ -36,9 +37,10 @@ void restart_game(MenuData *m, void *arg);
void create_gameover_menu(MenuData *m) {
create_menu(m);
m->draw = draw_ingame_menu;
m->flags = MF_Transient | MF_AlwaysProcessInput;
m->transition = NULL;
m->transition = TransEmpty;
if(global.stage->type == STAGE_SPELL) {
m->context = "Spell Failed";
@ -58,4 +60,6 @@ void create_gameover_menu(MenuData *m) {
if(!c)
m->cursor = 1;
}
set_transition(TransEmpty, 0, m->transition_out_time);
}

View file

@ -24,11 +24,13 @@ void restart_game(MenuData *m, void *arg) {
void create_ingame_menu(MenuData *m) {
create_menu(m);
m->draw = draw_ingame_menu;
m->flags = MF_Abortable | MF_Transient | MF_AlwaysProcessInput;
m->transition = NULL;
m->transition = TransEmpty;
add_menu_entry(m, "Return to Game", return_to_game, NULL);
add_menu_entry(m, "Restart the Game", restart_game, NULL)->transition = TransFadeBlack;
add_menu_entry(m, "Return to Title", return_to_title, NULL)->transition = TransFadeBlack;
set_transition(TransEmpty, 0, m->transition_out_time);
}
void draw_ingame_menu_bg(float f) {
@ -89,7 +91,3 @@ void draw_ingame_menu(MenuData *menu) {
draw_hud();
}
int ingame_menu_loop(MenuData *m) {
return menu_loop(m, NULL, draw_ingame_menu, NULL);
}

View file

@ -14,6 +14,5 @@ void draw_ingame_menu_bg(float f);
void create_ingame_menu(MenuData *menu);
void draw_ingame_menu(MenuData *menu);
int ingame_menu_loop(MenuData *menu);
#endif

View file

@ -22,25 +22,25 @@
void enter_options(MenuData *menu, void *arg) {
MenuData m;
create_options_menu(&m);
options_menu_loop(&m);
menu_loop(&m);
}
void enter_stagemenu(MenuData *menu, void *arg) {
MenuData m;
create_stage_menu(&m);
stage_menu_loop(&m);
menu_loop(&m);
}
void enter_replayview(MenuData *menu, void *arg) {
MenuData m;
create_replayview_menu(&m);
replayview_menu_loop(&m);
menu_loop(&m);
}
void enter_spellpractice(MenuData *menu, void *arg) {
MenuData m;
create_spell_menu(&m);
spell_menu_loop(&m);
menu_loop(&m);
}
static MenuEntry *spell_practice_entry;
@ -60,8 +60,15 @@ void main_menu_update_spellpractice(void) {
}
}
void begin_main_menu(MenuData *m) {
start_bgm("bgm_menu");
set_transition(TransLoader, 0, FADE_TIME*2);
}
void create_main_menu(MenuData *m) {
create_menu(m);
m->draw = draw_main_menu;
m->begin = begin_main_menu;
add_menu_entry(m, "Start Story", start_game, NULL);
add_menu_entry(m, "Start Extra", NULL, NULL);
@ -71,7 +78,7 @@ void create_main_menu(MenuData *m) {
#endif
add_menu_entry(m, "Replays", enter_replayview, NULL);
add_menu_entry(m, "Options", enter_options, NULL);
add_menu_entry(m, "Quit", (MenuAction)kill_menu, m);
add_menu_entry(m, "Quit", menu_commonaction_close, NULL)->transition = TransFadeBlack;;
spell_practice_entry = m->entries + 2;
main_menu_update_spellpractice();
@ -126,8 +133,3 @@ void draw_main_menu(MenuData *menu) {
glPopMatrix();
}
void main_menu_loop(MenuData *menu) {
set_transition(TransLoader, -1, FADE_TIME*2);
menu_loop(menu, NULL, draw_main_menu, NULL);
}

View file

@ -13,7 +13,6 @@
void create_main_menu(MenuData *m);
void draw_main_menu_bg(MenuData *m);
void draw_main_menu(MenuData *m);
void main_menu_loop(MenuData *m);
void main_menu_update_spellpractice(void);
#endif

View file

@ -5,40 +5,32 @@
* Copyright (C) 2011, Lukas Weber <laochailan@web.de>
*/
#include <assert.h>
#include "menu.h"
#include "global.h"
#include "video.h"
MenuEntry *add_menu_entry(MenuData *menu, char *name, MenuAction action, void *arg) {
return add_menu_entry_f(menu, name, action, arg, 0);
}
MenuEntry *add_menu_entry_f(MenuData *menu, char *name, MenuAction action, void *arg, int flags) {
menu->entries = realloc(menu->entries, (++menu->ecount)*sizeof(MenuEntry));
MenuEntry *e = &(menu->entries[menu->ecount-1]);
MenuEntry *e = menu->entries + menu->ecount - 1;
memset(e, 0, sizeof(MenuEntry));
e->name = malloc(strlen(name)+1);
strcpy(e->name, name);
stralloc(&e->name, name);
e->action = action;
e->arg = arg;
e->flags = flags;
e->transition = menu->transition;
return e;
}
void add_menu_separator(MenuData *menu) {
menu->entries = realloc(menu->entries, (++menu->ecount)*sizeof(MenuEntry));
memset(&(menu->entries[menu->ecount-1]), 0, sizeof(MenuEntry));
memset(menu->entries + menu->ecount - 1, 0, sizeof(MenuEntry));
}
void destroy_menu(MenuData *menu) {
int i;
for(i = 0; i < menu->ecount; i++) {
MenuEntry *e = &(menu->entries[i]);
free(e->name);
for(int i = 0; i < menu->ecount; i++) {
free(menu->entries[i].name);
}
free(menu->entries);
@ -48,33 +40,50 @@ void create_menu(MenuData *menu) {
memset(menu, 0, sizeof(MenuData));
menu->selected = -1;
menu->quitdelay = FADE_TIME;
menu->transition = TransFadeBlack;
menu->transition = TransMenu; // TransFadeBlack;
menu->transition_in_time = FADE_TIME;
menu->transition_out_time = FADE_TIME;
menu->fade = 1.0;
menu->logic = menu_logic;
menu->input = menu_input;
}
void close_menu_finish(MenuData *menu) {
menu->state = MS_Dead;
if(menu->selected != -1 && menu->entries[menu->selected].action != NULL) {
if(!(menu->flags & MF_Transient)) {
menu->state = MS_Normal;
}
menu->entries[menu->selected].action(menu, menu->entries[menu->selected].arg);
}
}
void close_menu(MenuData *menu) {
TransitionRule trans = menu->transition;
if(menu->selected != -1)
assert(menu->state != MS_Dead);
menu->state = MS_FadeOut;
if(menu->selected != -1) {
trans = menu->entries[menu->selected].transition;
}
set_transition(trans, menu->quitdelay, menu->quitdelay);
menu->quitframe = menu->frames;
}
void kill_menu(MenuData *menu) {
menu->state = MS_Dead;
if(trans) {
set_transition_callback(
trans,
menu->transition_in_time,
menu->transition_out_time,
(TransitionCallback)close_menu_finish, menu
);
} else {
close_menu_finish(menu);
}
}
float menu_fade(MenuData *menu) {
if(menu->frames < menu->quitdelay)
return 1.0 - menu->frames/(float)menu->quitdelay;
if(menu->quitframe && menu->frames >= menu->quitframe)
return (menu->frames - menu->quitframe)/(float)menu->quitdelay;
return 0.0;
return transition.fade;
}
void menu_event(EventType type, int state, void *arg) {
@ -121,48 +130,44 @@ void menu_input(MenuData *menu) {
handle_events(menu_event, EF_Menu, (void*)menu);
}
void menu_logic(MenuData *menu) {
menu->frames++;
if(menu->quitframe && menu->frames >= menu->quitframe)
menu->state = MS_FadeOut;
if(menu->quitframe && menu->frames >= menu->quitframe + menu->quitdelay*!(menu->state & MF_InstantSelect || menu->selected != -1 && menu->entries[menu->selected].flags & MF_InstantSelect)) {
menu->state = MS_Dead;
if(menu->selected != -1 && menu->entries[menu->selected].action != NULL) {
if(!(menu->flags & MF_Transient)) {
menu->state = MS_Normal;
menu->quitframe = 0;
}
menu->entries[menu->selected].action(menu, menu->entries[menu->selected].arg);
}
}
void menu_no_input(MenuData *menu) {
handle_events(NULL, 0, NULL);
}
int menu_loop(MenuData *menu, void (*input)(MenuData*), void (*draw)(MenuData*), void (*end)(MenuData*)) {
void menu_logic(MenuData *menu) {
menu->frames++;
}
int menu_loop(MenuData *menu) {
set_ortho();
if(menu->begin) {
menu->begin(menu);
}
while(menu->state != MS_Dead) {
menu_logic(menu);
assert(menu->logic);
menu->logic(menu);
if(menu->state != MS_FadeOut || menu->flags & MF_AlwaysProcessInput) {
if(input)
input(menu);
else
menu_input(menu);
} else handle_events(NULL, 0, NULL);
assert(menu->input);
menu->input(menu);
} else {
menu_no_input(menu);
}
draw(menu);
if(!(menu->flags & MF_ManualDrawTransition))
draw_transition();
assert(menu->draw);
menu->draw(menu);
draw_transition();
SDL_GL_SwapWindow(video.window);
frame_rate(&menu->lasttime);
}
if(end)
end(menu);
destroy_menu(menu);
if(menu->end) {
menu->end(menu);
}
destroy_menu(menu);
return menu->selected;
}

View file

@ -23,29 +23,20 @@ typedef struct MenuData MenuData;
typedef void (*MenuAction)(MenuData*, void*);
typedef bool (*MenuCallback)(MenuData*);
typedef void (*MenuProc)(MenuData*);
typedef struct MenuEntry {
char *name;
MenuAction action;
void *arg;
int flags;
float drawdata;
TransitionRule transition;
} MenuEntry;
// enum EntryFlag {
// MF_InstantSelect = 4
// };
enum MenuFlag {
MF_Transient = 1, // whether to close on selection or not.
MF_Abortable = 2,
MF_InstantSelect = 4,
MF_ManualDrawTransition = 8, // the menu will not call draw_transition() automatically
MF_AlwaysProcessInput = 16 // the menu will process input even during fadeouts
MF_Transient = 1, // the menu will be automatically closed on selection
MF_Abortable = 2, // the menu can be closed with the escape key
MF_AlwaysProcessInput = 4 // the menu will process input when it's fading out
};
enum MenuState {
@ -54,7 +45,7 @@ enum MenuState {
MS_Dead
};
struct MenuData{
struct MenuData {
int flags;
int cursor;
@ -67,14 +58,21 @@ struct MenuData{
int lasttime;
int state;
int quitframe;
int quitdelay;
int transition_in_time;
int transition_out_time;
float fade;
TransitionRule transition;
float drawdata[4];
void *context;
MenuProc draw;
MenuProc input;
MenuProc logic;
MenuProc begin;
MenuProc end;
};
MenuEntry *add_menu_entry(MenuData *menu, char *name, MenuAction action, void *arg);
@ -86,13 +84,13 @@ void destroy_menu(MenuData *menu);
void menu_logic(MenuData *menu);
void menu_input(MenuData *menu);
void menu_no_input(MenuData *menu);
void close_menu(MenuData *menu); // softly close menu (should be used in most cases)
void kill_menu(MenuData *menu); // quit action for persistent menus
void close_menu(MenuData *menu);
void menu_key_action(MenuData *menu, int sym);
int menu_loop(MenuData *menu, void (*input)(MenuData*), void (*draw)(MenuData*), void (*end)(MenuData*));
int menu_loop(MenuData *menu);
float menu_fade(MenuData *menu);

View file

@ -281,10 +281,16 @@ void destroy_options_menu(MenuData *m) {
static void do_nothing(MenuData *menu, void *arg) { }
void create_options_sub(MenuData *m, char *s) {
void options_menu_input(MenuData*);
void create_options_menu_basic(MenuData *m, char *s) {
create_menu(m);
m->transition = TransMenuDark;
m->flags = MF_Abortable;
m->context = s;
m->input = options_menu_input;
m->draw = draw_options_menu;
m->end = destroy_options_menu;
}
#define bind_onoff(b) bind_addvalue(b, "on"); bind_addvalue(b, "off")
@ -294,7 +300,7 @@ void options_sub_video(MenuData *parent, void *arg) {
OptionBinding *b;
m = &menu;
create_options_sub(m, "Video Options");
create_options_menu_basic(m, "Video Options");
add_menu_entry(m, "Resolution", do_nothing,
b = bind_resolution()
@ -332,7 +338,7 @@ void options_sub_video(MenuData *parent, void *arg) {
add_menu_separator(m);
add_menu_entry(m, "Back", menu_commonaction_close, NULL);
options_menu_loop(m);
menu_loop(m);
parent->frames = 0;
}
@ -352,7 +358,7 @@ void options_sub_gamepad_controls(MenuData *parent, void *arg) {
MenuData menu, *m;
m = &menu;
create_options_sub(m, "Gamepad Controls");
create_options_menu_basic(m, "Gamepad Controls");
add_menu_entry(m, "Fire / Accept", do_nothing,
bind_gpbinding(CONFIG_GAMEPAD_KEY_SHOT)
@ -397,7 +403,7 @@ void options_sub_gamepad_controls(MenuData *parent, void *arg) {
add_menu_separator(m);
add_menu_entry(m, "Back", menu_commonaction_close, NULL);
options_menu_loop(m);
menu_loop(m);
parent->frames = 0;
gamepad_restart();
}
@ -407,7 +413,7 @@ void options_sub_gamepad(MenuData *parent, void *arg) {
OptionBinding *b;
m = &menu;
create_options_sub(m, "Gamepad Options");
create_options_menu_basic(m, "Gamepad Options");
add_menu_entry(m, "Enable Gamepad/Joystick support", do_nothing,
b = bind_option(CONFIG_GAMEPAD_ENABLED, bind_common_onoffget, bind_common_onoffset)
@ -464,7 +470,7 @@ void options_sub_gamepad(MenuData *parent, void *arg) {
add_menu_separator(m);
add_menu_entry(m, "Back", menu_commonaction_close, NULL);
options_menu_loop(m);
menu_loop(m);
parent->frames = 0;
gamepad_restart();
}
@ -473,7 +479,7 @@ void options_sub_controls(MenuData *parent, void *arg) {
MenuData menu, *m;
m = &menu;
create_options_sub(m, "Controls");
create_options_menu_basic(m, "Controls");
add_menu_entry(m, "Move up", do_nothing,
bind_keybinding(CONFIG_KEY_UP)
@ -534,16 +540,14 @@ void options_sub_controls(MenuData *parent, void *arg) {
add_menu_separator(m);
add_menu_entry(m, "Back", menu_commonaction_close, NULL);
options_menu_loop(m);
menu_loop(m);
parent->frames = 0;
}
void create_options_menu(MenuData *m) {
OptionBinding *b;
create_menu(m);
m->flags = MF_Abortable;
m->context = "Options";
create_options_menu_basic(m, "Options");
add_menu_entry(m, "Player name", do_nothing,
b = bind_stroption(CONFIG_PLAYERNAME)
@ -943,8 +947,3 @@ void options_menu_input(MenuData *menu) {
} else
handle_events(options_input_event, EF_Menu, menu);
}
int options_menu_loop(MenuData *menu) {
return menu_loop(menu, options_menu_input, draw_options_menu, destroy_options_menu);
}

View file

@ -12,9 +12,7 @@
#include "menu.h"
void create_options_menu(MenuData *m);
void draw_options_menu(MenuData *m);
int options_menu_loop(MenuData *m);
#define OPTIONS_TEXT_INPUT_BUFSIZE 50

View file

@ -22,6 +22,7 @@
typedef struct ReplayviewContext {
MenuData *submenu;
int pickedstage;
double sub_fade;
} ReplayviewContext;
// Type of MenuEntry.arg (which should be renamed to context, probably...)
@ -44,14 +45,17 @@ void start_replay(MenuData *menu, void *arg) {
start_bgm("bgm_menu");
}
static void replayview_draw_stagemenu(MenuData*);
MenuData* replayview_sub_stageselect(MenuData *menu, ReplayviewItemContext *ictx) {
MenuData *m = malloc(sizeof(MenuData));
Replay *rpy = ictx->replay;
create_menu(m);
m->draw = replayview_draw_stagemenu;
m->context = menu->context;
m->flags = MF_Transient | MF_Abortable;
m->transition = 0;
m->transition = NULL;
for(int i = 0; i < rpy->numstages; ++i) {
add_menu_entry(m, stage_get(rpy->stages[i].stage)->title, start_replay, ictx)->transition = TransFadeBlack;
@ -99,7 +103,7 @@ static void replayview_draw_stagemenu(MenuData *m) {
// this context is shared with the parent menu
ReplayviewContext *ctx = m->context;
float alpha = 1-menu_fade(m);
float alpha = 1 - ctx->sub_fade;
int i;
float height = (1+m->ecount) * 20;
@ -209,6 +213,26 @@ static void replayview_drawitem(void *n, int item, int cnt) {
}
}
static void replayview_logic(MenuData *m) {
ReplayviewContext *ctx = m->context;
if(ctx->submenu) {
MenuData *sm = ctx->submenu;
if(sm->state == MS_Dead) {
if(ctx->sub_fade == 1.0) {
destroy_menu(sm);
ctx->submenu = NULL;
return;
}
ctx->sub_fade = approach(ctx->sub_fade, 1.0, 1.0/FADE_TIME);
} else {
ctx->sub_fade = approach(ctx->sub_fade, 0.0, 1.0/FADE_TIME);
}
}
}
static void replayview_draw(MenuData *m) {
ReplayviewContext *ctx = m->context;
@ -222,14 +246,7 @@ static void replayview_draw(MenuData *m) {
draw_menu_list(m, 100, 100, replayview_drawitem);
if(ctx->submenu) {
MenuData *sm = ctx->submenu;
menu_logic(sm);
if(sm->state == MS_Dead) {
destroy_menu(sm);
ctx->submenu = NULL;
} else {
replayview_draw_stagemenu(sm);
}
ctx->submenu->draw(ctx->submenu);
}
}
@ -273,7 +290,7 @@ int fill_replayview_menu(MenuData *m) {
ictx->replayname = malloc(strlen(e->d_name) + 1);
strcpy(ictx->replayname, e->d_name);
add_menu_entry_f(m, " ", replayview_run, ictx, (rpy->numstages > 1)*MF_InstantSelect)->transition = rpy->numstages < 2 ? TransFadeBlack : NULL;
add_menu_entry(m, " ", replayview_run, ictx)->transition = rpy->numstages < 2 ? TransFadeBlack : NULL;
++rpys;
}
@ -282,8 +299,41 @@ int fill_replayview_menu(MenuData *m) {
return rpys;
}
void replayview_menu_input(MenuData *m) {
ReplayviewContext *ctx = (ReplayviewContext*)m->context;
MenuData *sub = ctx->submenu;
if(transition.state == TRANS_FADE_IN) {
menu_no_input(m);
} else {
if(sub && sub->state != MS_Dead) {
sub->input(sub);
} else {
menu_input(m);
}
}
}
void replayview_free(MenuData *m) {
if(m->context) {
free(m->context);
m->context = NULL;
}
for(int i = 0; i < m->ecount; i++) {
if(m->entries[i].action == replayview_run) {
replayview_freearg(m->entries[i].arg);
}
}
}
void create_replayview_menu(MenuData *m) {
create_menu(m);
m->logic = replayview_logic;
m->input = replayview_menu_input;
m->draw = replayview_draw;
m->end = replayview_free;
m->transition = TransMenuDark;
ReplayviewContext *ctx = malloc(sizeof(ReplayviewContext));
memset(ctx, 0, sizeof(ReplayviewContext));
@ -302,26 +352,3 @@ void create_replayview_menu(MenuData *m) {
add_menu_entry(m, "Back", menu_commonaction_close, NULL);
}
}
void replayview_menu_input(MenuData *m) {
ReplayviewContext *ctx = (ReplayviewContext*)m->context;
menu_input(ctx->submenu ? ctx->submenu : m);
}
void replayview_free(MenuData *m) {
if(m->context) {
free(m->context);
m->context = NULL;
}
for(int i = 0; i < m->ecount; i++) {
if(m->entries[i].action == replayview_run) {
replayview_freearg(m->entries[i].arg);
}
}
}
int replayview_menu_loop(MenuData *m) {
return menu_loop(m, replayview_menu_input, replayview_draw, replayview_free);
}

View file

@ -9,7 +9,8 @@
#ifndef RPYVIEW_H
#define RPYVIEW_H
#include "menu.h"
void create_replayview_menu(MenuData *m);
int replayview_menu_loop(MenuData *m);
#endif

View file

@ -38,15 +38,6 @@ void save_rpy(MenuData *menu, void *a) {
replay_save(rpy, name);
}
void create_saverpy_menu(MenuData *m) {
create_menu(m);
m->flags = MF_Transient;
add_menu_entry(m, "Yes", save_rpy, m);
add_menu_entry(m, "No", (MenuAction) kill_menu, m);
}
void draw_saverpy_menu(MenuData *m) {
int i;
@ -95,6 +86,13 @@ void saverpy_menu_input(MenuData *menu) {
handle_events(saverpy_input_event, EF_Menu, menu);
}
int saverpy_menu_loop(MenuData *m) {
return menu_loop(m, saverpy_menu_input, draw_saverpy_menu, NULL);
void create_saverpy_menu(MenuData *m) {
create_menu(m);
m->input = saverpy_menu_input;
m->draw = draw_saverpy_menu;
m->flags = MF_Transient;
add_menu_entry(m, "Yes", save_rpy, NULL);
add_menu_entry(m, "No", menu_commonaction_close, NULL);
}

View file

@ -13,6 +13,5 @@
void save_rpy(MenuData *menu, void*);
void create_saverpy_menu(MenuData*);
int saverpy_menu_loop(MenuData*);
#endif

View file

@ -10,12 +10,21 @@
#include "options.h"
#include "global.h"
void draw_spell_menu(MenuData *m) {
draw_options_menu_bg(m);
draw_menu_title(m, "Spell Practice");
animate_menu_list(m);
draw_menu_list(m, 100, 100, NULL);
}
void create_spell_menu(MenuData *m) {
char title[128];
Difficulty lastdiff = D_Any;
create_menu(m);
m->draw = draw_spell_menu;
m->flags = MF_Abortable;
m->transition = TransMenuDark;
for(StageInfo *stg = stages; stg->loop; ++stg) {
if(stg->type != STAGE_SPELL) {
@ -45,14 +54,3 @@ void create_spell_menu(MenuData *m) {
++m->cursor;
}
}
void draw_spell_menu(MenuData *m) {
draw_options_menu_bg(m);
draw_menu_title(m, "Spell Practice");
animate_menu_list(m);
draw_menu_list(m, 100, 100, NULL);
}
int spell_menu_loop(MenuData *m) {
return menu_loop(m, NULL, draw_spell_menu, NULL);
}

View file

@ -11,6 +11,5 @@
#include "menu.h"
void create_spell_menu(MenuData *m);
int spell_menu_loop(MenuData *m);
#endif

View file

@ -13,12 +13,21 @@
#include "stageselect.h"
#include "common.h"
void draw_stage_menu(MenuData *m) {
draw_options_menu_bg(m);
draw_menu_title(m, "Select Stage");
animate_menu_list(m);
draw_menu_list(m, 100, 100, NULL);
}
void create_stage_menu(MenuData *m) {
char title[STGMENU_MAX_TITLE_LENGTH];
Difficulty lastdiff = D_Any;
create_menu(m);
m->draw = draw_stage_menu;
m->flags = MF_Abortable;
m->transition = TransMenuDark;
for(int i = 0; stages[i].loop; ++i) {
if(stages[i].difficulty < lastdiff || (stages[i].difficulty && !lastdiff)) {
@ -34,14 +43,3 @@ void create_stage_menu(MenuData *m) {
add_menu_separator(m);
add_menu_entry(m, "Back", menu_commonaction_close, NULL);
}
void draw_stage_menu(MenuData *m) {
draw_options_menu_bg(m);
draw_menu_title(m, "Select Stage");
animate_menu_list(m);
draw_menu_list(m, 100, 100, NULL);
}
int stage_menu_loop(MenuData *m) {
return menu_loop(m, NULL, draw_stage_menu, NULL);
}

View file

@ -9,9 +9,10 @@
#ifndef STGMENU_H
#define STGMENU_H
#include "menu.h"
#define STGMENU_MAX_TITLE_LENGTH 128
void create_stage_menu(MenuData *m);
int stage_menu_loop(MenuData *m);
#endif

View file

@ -49,6 +49,9 @@ int init_mixer_if_needed(void) {
warnx("Mix_OpenAudio(): %s.\n", Mix_GetError());
}
set_sfx_volume(config_get_float(CONFIG_SFX_VOLUME));
set_bgm_volume(config_get_float(CONFIG_BGM_VOLUME));
mixer_loaded = 1;
return 1;
}
@ -86,7 +89,7 @@ int init_sfx(void)
if (config_get_int(CONFIG_NO_AUDIO)) return 1;
if (!init_mixer_if_needed()) return 0;
int channels = Mix_AllocateChannels(SNDCHAN_COUNT);
if (!channels)
{
@ -96,7 +99,7 @@ int init_sfx(void)
return 0;
}
if (channels < SNDCHAN_COUNT) warnx("init_sfx(): allocated only %d of %d channels.\n", channels, SNDCHAN_COUNT);
return 1;
}

View file

@ -324,16 +324,16 @@ void stage_pause(void) {
MenuData menu;
stop_bgm();
create_ingame_menu(&menu);
ingame_menu_loop(&menu);
menu_loop(&menu);
continue_bgm();
}
void stage_gameover(void) {
MenuData m;
MenuData menu;
save_bgm();
start_bgm("bgm_gameover");
create_gameover_menu(&m);
ingame_menu_loop(&m);
create_gameover_menu(&menu);
menu_loop(&menu);
restore_bgm();
}

View file

@ -9,48 +9,120 @@
#include "menu/ingamemenu.h"
#include "global.h"
static Transition transition;
float trans_fade(Transition *t) {
if(t->frames <= t->dur1)
return t->frames/(float)t->dur1;
else
return (t->dur1+t->dur2-t->frames)/(float)t->dur2;
}
Transition transition;
void TransFadeBlack(Transition *t) {
fade_out(trans_fade(t));
fade_out(t->fade);
}
void TransFadeWhite(Transition *t) {
colorfill(1,1,1,trans_fade(t));
colorfill(1,1,1,t->fade);
}
void TransLoader(Transition *t) {
glColor4f(1,1,1,trans_fade(t));
glColor4f(1,1,1,t->fade);
draw_texture(SCREEN_W/2,SCREEN_H/2,"loading");
glColor4f(1,1,1,1);
}
void set_transition(TransitionRule rule, int dur1, int dur2) {
if(!rule)
return;
void TransMenu(Transition *t) {
glColor4f(1,1,1,t->fade);
draw_texture(SCREEN_W/2, SCREEN_H/2, "mainmenu/mainmenubg");
glColor4f(1, 1, 1, 1);
}
memset(&transition, 0, sizeof(Transition));
transition.rule = rule;
transition.dur1 = dur1;
transition.dur2 = dur2;
void TransMenuDark(Transition *t) {
glColor4f(0.3,0.3,0.3,t->fade);
draw_texture(SCREEN_W/2, SCREEN_H/2, "mainmenu/mainmenubg");
glColor4f(1, 1, 1, 1);
}
void TransEmpty(Transition *t) { }
static bool popq(void) {
if(transition.queued.rule) {
transition.rule = transition.queued.rule;
transition.dur1 = transition.queued.dur1;
transition.dur2 = transition.queued.dur2;
transition.callback = transition.queued.callback;
transition.arg = transition.queued.arg;
transition.queued.rule = NULL;
if(transition.dur1 <= 0) {
transition.fade = 1.0;
transition.state = TRANS_FADE_OUT;
} else {
transition.state = TRANS_FADE_IN;
}
return true;
}
return false;
}
static void setq(TransitionRule rule, int dur1, int dur2, TransitionCallback cb, void *arg) {
transition.queued.rule = rule;
transition.queued.dur1 = dur1;
transition.queued.dur2 = dur2;
transition.queued.callback = cb;
transition.queued.arg = arg;
}
static void call_callback(void) {
if(transition.callback) {
transition.callback(transition.arg);
transition.callback = NULL;
}
}
void set_transition_callback(TransitionRule rule, int dur1, int dur2, TransitionCallback cb, void *arg) {
static bool initialized = false;
if(!rule) {
return;
}
setq(rule, dur1, dur2, cb, arg);
if(!initialized) {
popq();
initialized = true;
}
if(transition.state == TRANS_IDLE || rule == transition.rule) {
popq();
}
}
void set_transition(TransitionRule rule, int dur1, int dur2) {
set_transition_callback(rule, dur1, dur2, NULL, NULL);
}
void draw_transition(void) {
if(!transition.rule)
if(transition.state == TRANS_IDLE) {
return;
}
if(!transition.rule) {
transition.state = TRANS_IDLE;
return;
}
transition.rule(&transition);
transition.frames++;
if(transition.frames > transition.dur1 + transition.dur2)
memset(&transition, 0, sizeof(Transition));
if(transition.state == TRANS_FADE_IN) {
transition.fade = approach(transition.fade, 1.0, 1.0/transition.dur1);
if(transition.fade == 1.0) {
transition.state = TRANS_FADE_OUT;
call_callback();
}
} else if(transition.state == TRANS_FADE_OUT) {
transition.fade = transition.dur2 ? approach(transition.fade, 0.0, 1.0/transition.dur2) : 0.0;
if(transition.fade == 0.0) {
transition.state = TRANS_IDLE;
popq();
}
}
}

View file

@ -8,22 +8,47 @@
#ifndef TRANSITION_H
#define TRANSITION_H
#include <stdbool.h>