1 /*
2 * This software is licensed under the terms of the MIT License.
3 * See COPYING for further information.
4 * ---
5 * Copyright (c) 2011-2019, Lukas Weber <laochailan@web.de>.
6 * Copyright (c) 2012-2019, Andrei Alexeyev <akari@taisei-project.org>.
7 */
8
9 #include "taisei.h"
10
11 #include "menu.h"
12 #include "common.h"
13 #include "ingamemenu.h"
14 #include "submenus.h"
15 #include "global.h"
16 #include "stagedraw.h"
17 #include "video.h"
18 #include "options.h"
19 #include "renderer/api.h"
20
return_to_title(MenuData * m,void * arg)21 static void return_to_title(MenuData *m, void *arg) {
22 global.gameover = GAMEOVER_ABORT;
23 menu_action_close(m, arg);
24 }
25
restart_game(MenuData * m,void * arg)26 void restart_game(MenuData *m, void *arg) {
27 global.gameover = GAMEOVER_RESTART;
28 menu_action_close(m, arg);
29 }
30
ingame_menu_do(MenuData * m,MenuAction action)31 static void ingame_menu_do(MenuData *m, MenuAction action) {
32 m->selected = -1;
33
34 dynarray_foreach(&m->entries, int i, MenuEntry *e, {
35 if(e->action == action) {
36 m->selected = i;
37 }
38 });
39
40 assert(m->selected >= 0);
41 close_menu(m);
42 }
43
check_shortcut(SDL_Scancode scan,SDL_Scancode expectscan)44 static bool check_shortcut(SDL_Scancode scan, SDL_Scancode expectscan) {
45 if(scan != expectscan) {
46 return false;
47 }
48
49 KeyIndex k = config_key_from_scancode(scan);
50
51 switch(k) {
52 case KEY_UP:
53 case KEY_DOWN:
54 case KEY_LEFT:
55 case KEY_RIGHT:
56 case KEY_SHOT:
57 case KEY_BOMB:
58 return false;
59
60 default:
61 return true;
62 }
63 }
64
ingame_menu_input_handler(SDL_Event * event,void * arg)65 static bool ingame_menu_input_handler(SDL_Event *event, void *arg) {
66 MenuData *menu = arg;
67
68 switch(event->type) {
69 case SDL_KEYDOWN: {
70 SDL_Scancode scan = event->key.keysym.scancode;
71
72 if(check_shortcut(scan, SDL_SCANCODE_R)) {
73 ingame_menu_do(menu, restart_game);
74 return true;
75 }
76
77 if(check_shortcut(scan, SDL_SCANCODE_Q)) {
78 ingame_menu_do(menu, return_to_title);
79 return true;
80 }
81
82 return false;
83 }
84
85 default: {
86 return false;
87 }
88 }
89 }
90
ingame_menu_input_filter(SDL_Event * event,void * arg)91 static bool ingame_menu_input_filter(SDL_Event *event, void *arg) {
92 MenuData *menu = arg;
93
94 if(menu->state == MS_Normal) {
95 return false;
96 }
97
98 switch(TAISEI_EVENT(event->type)) {
99 // workaround for #136
100 case TE_MENU_ACCEPT:
101 return true;
102 }
103
104 return false;
105 }
106
ingame_menu_input(MenuData * m)107 static void ingame_menu_input(MenuData *m) {
108 events_poll((EventHandler[]) {
109 { .proc = ingame_menu_input_filter, .arg = m },
110 { .proc = menu_input_handler, .arg = m },
111 { .proc = ingame_menu_input_handler, .arg = m },
112 { NULL }
113 }, EFLAG_MENU);
114 }
115
create_ingame_menu(void)116 MenuData* create_ingame_menu(void) {
117 MenuData *m = alloc_menu();
118
119 m->draw = draw_ingame_menu;
120 m->logic = update_ingame_menu;
121 m->input = ingame_menu_input;
122 m->flags = MF_Abortable | MF_AlwaysProcessInput;
123 m->transition = TransEmpty;
124 m->cursor = 1;
125 m->context = "Game Paused";
126 add_menu_entry(m, "Options", menu_action_enter_options, NULL)->transition = TransFadeBlack;
127 add_menu_entry(m, "Return to Game", menu_action_close, NULL);
128 add_menu_entry(m, "Restart the Game", restart_game, NULL)->transition = TransFadeBlack;
129 add_menu_entry(m, "Stop the Game", return_to_title, NULL)->transition = TransFadeBlack;
130 set_transition(TransEmpty, 0, m->transition_out_time);
131
132 return m;
133 }
134
skip_stage(MenuData * m,void * arg)135 static void skip_stage(MenuData *m, void *arg) {
136 global.gameover = GAMEOVER_WIN;
137 menu_action_close(m, arg);
138 }
139
create_ingame_menu_replay(void)140 MenuData* create_ingame_menu_replay(void) {
141 MenuData *m = alloc_menu();
142
143 m->draw = draw_ingame_menu;
144 m->logic = update_ingame_menu;
145 m->flags = MF_Abortable | MF_AlwaysProcessInput;
146 m->transition = TransEmpty;
147 m->cursor = 1;
148 m->context = "Replay Paused";
149 add_menu_entry(m, "Options", menu_action_enter_options, NULL)->transition = TransFadeBlack;
150 add_menu_entry(m, "Continue Watching", menu_action_close, NULL);
151 add_menu_entry(m, "Restart the Stage", restart_game, NULL)->transition = TransFadeBlack;
152 add_menu_entry(m, "Skip the Stage", skip_stage, NULL)->transition = TransFadeBlack;
153 add_menu_entry(m, "Stop Watching", return_to_title, NULL)->transition = TransFadeBlack;
154 m->cursor = 1;
155 set_transition(TransEmpty, 0, m->transition_out_time);
156
157 return m;
158 }
159
draw_ingame_menu_bg(MenuData * menu,float f)160 void draw_ingame_menu_bg(MenuData *menu, float f) {
161 float rad = f*IMENU_BLUR;
162
163 r_state_push();
164 r_shader("ingame_menu");
165 r_uniform_float("rad", rad);
166 r_uniform_float("phase", menu->frames / 100.0);
167 stage_draw_viewport();
168 r_state_pop();
169 }
170
update_ingame_menu(MenuData * menu)171 void update_ingame_menu(MenuData *menu) {
172 menu->drawdata[0] += (menu->cursor*35 - menu->drawdata[0])/7.0;
173 menu->drawdata[1] += (text_width(get_font("standard"), dynarray_get(&menu->entries, menu->cursor).name, 0) - menu->drawdata[1])/10.0;
174 }
175
draw_ingame_menu(MenuData * menu)176 void draw_ingame_menu(MenuData *menu) {
177 set_ortho(SCREEN_W, SCREEN_H);
178 draw_ingame_menu_bg(menu, 1.0-menu_fade(menu));
179 r_state_push();
180
181 r_mat_mv_push();
182 r_mat_mv_translate(VIEWPORT_X, VIEWPORT_Y, 0);
183 r_mat_mv_translate(VIEWPORT_W/2, VIEWPORT_H/4, 0);
184
185 draw_menu_selector(0, menu->drawdata[0], menu->drawdata[1]*2, 41, menu->frames);
186
187 r_shader("text_default");
188
189 if(menu->context) {
190 float s = 0.3 + 0.2 * sin(menu->frames/10.0);
191 r_color(RGBA_MUL_ALPHA(1-s/2, 1-s/2, 1-s, 1-menu_fade(menu)));
192 text_draw(menu->context, &(TextParams) {
193 .align = ALIGN_CENTER,
194 .pos = { 0, -2 * 35 },
195 });
196 }
197
198 dynarray_foreach(&menu->entries, int i, MenuEntry *e, {
199 if(e->action) {
200 float s = 0, t = 0.7;
201 if(i == menu->cursor) {
202 t = 1;
203 s = 0.3 + 0.2*sin(menu->frames/7.0);
204 }
205
206 r_color(RGBA_MUL_ALPHA(t-s, t-s, t-s/2, 1-menu_fade(menu)));
207 } else {
208 r_color(RGBA_MUL_ALPHA(0.5, 0.5, 0.5, 0.5 * (1-menu_fade(menu))));
209 }
210
211 text_draw(e->name, &(TextParams) {
212 .align = ALIGN_CENTER,
213 .pos = { 0, i * 35 },
214 });
215 });
216
217 r_mat_mv_pop();
218 r_state_pop();
219
220 // TODO handle dialog somehow
221
222 stage_draw_hud();
223 stage_draw_bottom_text();
224 }
225