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