1 /*
2 Copyright (C) 2009-2021 Parallel Realities
3 
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8 
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12 
13 See the GNU General Public License for more details.
14 
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA.
18 */
19 
20 #include "../headers.h"
21 
22 #include "../audio/audio.h"
23 #include "../draw.h"
24 #include "../graphics/graphics.h"
25 #include "../init.h"
26 #include "../input.h"
27 #include "../system/error.h"
28 #include "label.h"
29 #include "ok_menu.h"
30 #include "options_menu.h"
31 #include "widget.h"
32 
33 extern Input input, menuInput;
34 extern Game game;
35 extern Control control;
36 
37 static Menu menu;
38 
39 static void loadMenuLayout(void);
40 static void showOptionsMenu(void);
41 static void doMenu(void);
42 static void redefineKey(void);
43 static void realignGrid(void);
44 static char *getDeadZoneValue(int);
45 static void raiseDeadZoneValue(void);
46 static void lowerDeadZoneValue(void);
47 static void showControlMenu(void);
48 
drawControlMenu()49 void drawControlMenu()
50 {
51 	int i;
52 
53 	drawImage(menu.background, menu.x, menu.y, FALSE, 196);
54 
55 	for (i=0;i<menu.widgetCount;i++)
56 	{
57 		drawWidget(menu.widgets[i], &menu, menu.index == i);
58 	}
59 }
60 
doMenu()61 static void doMenu()
62 {
63 	Widget *w;
64 	int left, right, up, down, attack, xAxisMoved, yAxisMoved;
65 
66 	left = FALSE;
67 	right = FALSE;
68 	up = FALSE;
69 	down = FALSE;
70 	attack = FALSE;
71 
72 	if (menuInput.left == TRUE)
73 	{
74 		left = TRUE;
75 	}
76 
77 	else if (menuInput.right == TRUE)
78 	{
79 		right = TRUE;
80 	}
81 
82 	else if (menuInput.up == TRUE)
83 	{
84 		up = TRUE;
85 	}
86 
87 	else if (menuInput.down == TRUE)
88 	{
89 		down = TRUE;
90 	}
91 
92 	else if (menuInput.attack == TRUE)
93 	{
94 		attack = TRUE;
95 	}
96 
97 	else if (input.left == TRUE)
98 	{
99 		left = TRUE;
100 	}
101 
102 	else if (input.right == TRUE)
103 	{
104 		right = TRUE;
105 	}
106 
107 	else if (input.up == TRUE)
108 	{
109 		up = TRUE;
110 	}
111 
112 	else if (input.down == TRUE)
113 	{
114 		down = TRUE;
115 	}
116 
117 	else if (input.attack == TRUE)
118 	{
119 		attack = TRUE;
120 	}
121 
122 	if (down == TRUE)
123 	{
124 		menu.index++;
125 
126 		if (menu.index == menu.widgetCount)
127 		{
128 			menu.index = 1;
129 		}
130 
131 		playSound("sound/common/click");
132 	}
133 
134 	else if (up == TRUE)
135 	{
136 		menu.index--;
137 
138 		if (menu.index < 1)
139 		{
140 			menu.index = menu.widgetCount - 1;
141 		}
142 
143 		playSound("sound/common/click");
144 	}
145 
146 	else if (attack == TRUE)
147 	{
148 		w = menu.widgets[menu.index];
149 
150 		if (w->clickAction != NULL)
151 		{
152 			playSound("sound/common/click");
153 
154 			w->clickAction();
155 		}
156 	}
157 
158 	else if (left == TRUE)
159 	{
160 		w = menu.widgets[menu.index];
161 
162 		if (w->leftAction != NULL)
163 		{
164 			w->leftAction();
165 		}
166 
167 		playSound("sound/common/click");
168 	}
169 
170 	else if (right == TRUE)
171 	{
172 		w = menu.widgets[menu.index];
173 
174 		if (w->rightAction != NULL)
175 		{
176 			w->rightAction();
177 		}
178 
179 		playSound("sound/common/click");
180 	}
181 
182 	xAxisMoved = input.xAxisMoved;
183 	yAxisMoved = input.yAxisMoved;
184 
185 	memset(&menuInput, 0, sizeof(Input));
186 	memset(&input, 0, sizeof(Input));
187 
188 	input.xAxisMoved = xAxisMoved;
189 	input.yAxisMoved = yAxisMoved;
190 }
191 
loadMenuLayout()192 static void loadMenuLayout()
193 {
194 	char *text;
195 	int y;
196 
197 	y = 0;
198 
199 	menu.widgetCount = 16;
200 
201 	menu.widgets = malloc(sizeof(Widget *) * menu.widgetCount);
202 
203 	if (menu.widgets == NULL)
204 	{
205 		showErrorAndExit("Ran out of memory when creating Control Menu");
206 	}
207 
208 	menu.widgets[0] = createWidget(_("Use Up and Down to select. Enter to change."), NULL, NULL, NULL, &redefineKey, -1, y, FALSE, 255, 255, 255);
209 
210 	menu.widgets[1] = createWidget(_("Up"), &control.button[CONTROL_UP], NULL, NULL, &redefineKey, 20, y, TRUE, 255, 255, 255);
211 
212 	text = getKeyValue(control.button[CONTROL_UP]);
213 
214 	menu.widgets[1]->label = createLabel(text, menu.widgets[0]->x, y);
215 
216 	menu.widgets[2] = createWidget(_("Down"), &control.button[CONTROL_DOWN], NULL, NULL, &redefineKey, 20, y, TRUE, 255, 255, 255);
217 
218 	text = getKeyValue(control.button[CONTROL_DOWN]);
219 
220 	menu.widgets[2]->label = createLabel(text, menu.widgets[1]->x, y);
221 
222 	menu.widgets[3] = createWidget(_("Left"), &control.button[CONTROL_LEFT], NULL, NULL, &redefineKey, 20, y, TRUE, 255, 255, 255);
223 
224 	text = getKeyValue(control.button[CONTROL_LEFT]);
225 
226 	menu.widgets[3]->label = createLabel(text, menu.widgets[2]->x, y);
227 
228 	menu.widgets[4] = createWidget(_("Right"), &control.button[CONTROL_RIGHT], NULL, NULL, &redefineKey, 20, y, TRUE, 255, 255, 255);
229 
230 	text = getKeyValue(control.button[CONTROL_RIGHT]);
231 
232 	menu.widgets[4]->label = createLabel(text, menu.widgets[3]->x, y);
233 
234 	menu.widgets[5] = createWidget(_("Attack"), &control.button[CONTROL_ATTACK], NULL, NULL, &redefineKey, 20, y, TRUE, 255, 255, 255);
235 
236 	text = getKeyValue(control.button[CONTROL_ATTACK]);
237 
238 	menu.widgets[5]->label = createLabel(text, menu.widgets[4]->x, y);
239 
240 	menu.widgets[6] = createWidget(_("Block"), &control.button[CONTROL_BLOCK], NULL, NULL, &redefineKey, 20, y, TRUE, 255, 255, 255);
241 
242 	text = getKeyValue(control.button[CONTROL_BLOCK]);
243 
244 	menu.widgets[6]->label = createLabel(text, menu.widgets[5]->x, y);
245 
246 	menu.widgets[7] = createWidget(_("Jump"), &control.button[CONTROL_JUMP], NULL, NULL, &redefineKey, 40, y, TRUE, 255, 255, 255);
247 
248 	text = getKeyValue(control.button[CONTROL_JUMP]);
249 
250 	menu.widgets[7]->label = createLabel(text, menu.widgets[6]->x, y);
251 
252 	menu.widgets[8] = createWidget(_("Interact"), &control.button[CONTROL_INTERACT], NULL, NULL, &redefineKey, 40, y, TRUE, 255, 255, 255);
253 
254 	text = getKeyValue(control.button[CONTROL_INTERACT]);
255 
256 	menu.widgets[8]->label = createLabel(text, menu.widgets[7]->x, y);
257 
258 	menu.widgets[9] = createWidget(_("Use"), &control.button[CONTROL_ACTIVATE], NULL, NULL, &redefineKey, 40, y, TRUE, 255, 255, 255);
259 
260 	text = getKeyValue(control.button[CONTROL_ACTIVATE]);
261 
262 	menu.widgets[9]->label = createLabel(text, menu.widgets[8]->x, y);
263 
264 	menu.widgets[10] = createWidget(_("Previous Item"), &control.button[CONTROL_PREVIOUS], NULL, NULL, &redefineKey, 40, y, TRUE, 255, 255, 255);
265 
266 	text = getKeyValue(control.button[CONTROL_PREVIOUS]);
267 
268 	menu.widgets[10]->label = createLabel(text, menu.widgets[9]->x, y);
269 
270 	menu.widgets[11] = createWidget(_("Next Item"), &control.button[CONTROL_NEXT], NULL, NULL, &redefineKey, 40, y, TRUE, 255, 255, 255);
271 
272 	text = getKeyValue(control.button[CONTROL_NEXT]);
273 
274 	menu.widgets[11]->label = createLabel(text, menu.widgets[10]->x, y);
275 
276 	menu.widgets[12] = createWidget(_("Inventory"), &control.button[CONTROL_INVENTORY], NULL, NULL, &redefineKey, 40, y, TRUE, 255, 255, 255);
277 
278 	text = getKeyValue(control.button[CONTROL_INVENTORY]);
279 
280 	menu.widgets[12]->label = createLabel(text, menu.widgets[11]->x, y);
281 
282 	menu.widgets[13] = createWidget(_("Pause"), &control.button[CONTROL_PAUSE], NULL, NULL, &redefineKey, 40, y, TRUE, 255, 255, 255);
283 
284 	text = getKeyValue(control.button[CONTROL_PAUSE]);
285 
286 	menu.widgets[13]->label = createLabel(text, menu.widgets[12]->x, y);
287 
288 	menu.widgets[14] = createWidget(_("Dead Zone"), &control.deadZone, &lowerDeadZoneValue, &raiseDeadZoneValue, NULL, 40, y, TRUE, 255, 255, 255);
289 
290 	text = getDeadZoneValue(control.deadZone);
291 
292 	menu.widgets[14]->label = createLabel(text, menu.widgets[13]->x + menu.widgets[13]->normalState->w + 10, y);
293 
294 	menu.widgets[15] = createWidget(_("Back"), NULL, NULL, NULL, &showOptionsMenu, -1, y, TRUE, 255, 255, 255);
295 
296 	realignGrid();
297 }
298 
initControlMenu()299 Menu *initControlMenu()
300 {
301 	menu.action = &doMenu;
302 
303 	if (menu.widgets == NULL)
304 	{
305 		loadMenuLayout();
306 	}
307 
308 	menu.returnAction = &showOptionsMenu;
309 
310 	menu.index = 1;
311 
312 	return &menu;
313 }
314 
realignGrid()315 static void realignGrid()
316 {
317 	int i, y, w, maxWidth1, maxWidth2, colWidth1, colWidth2;
318 
319 	y = BUTTON_PADDING + BORDER_PADDING;
320 
321 	maxWidth1 = maxWidth2 = w = 0;
322 
323 	colWidth1 = colWidth2 = 0;
324 
325 	menu.widgets[0]->y = y = BUTTON_PADDING + BORDER_PADDING;
326 
327 	colWidth2 = menu.widgets[0]->selectedState->w;
328 
329 	y += menu.widgets[0]->selectedState->h + BUTTON_PADDING;
330 
331 	for (i=1;i<8;i++)
332 	{
333 		if (menu.widgets[i]->label != NULL && menu.widgets[i]->normalState->w > maxWidth1)
334 		{
335 			maxWidth1 = menu.widgets[i]->normalState->w;
336 		}
337 	}
338 
339 	for (i=1;i<8;i++)
340 	{
341 		menu.widgets[i]->y = y;
342 
343 		if (menu.widgets[i]->x != -1)
344 		{
345 			menu.widgets[i]->x = BUTTON_PADDING + BORDER_PADDING;
346 		}
347 
348 		if (menu.widgets[i]->label != NULL)
349 		{
350 			menu.widgets[i]->label->y = y;
351 
352 			menu.widgets[i]->label->x = menu.widgets[i]->x + maxWidth1 + 10;
353 
354 			if (menu.widgets[i]->label->x + menu.widgets[i]->label->text->w > colWidth1)
355 			{
356 				colWidth1 = menu.widgets[i]->label->x + menu.widgets[i]->label->text->w;
357 			}
358 		}
359 
360 		else
361 		{
362 			if (menu.widgets[i]->x + menu.widgets[i]->selectedState->w > colWidth1)
363 			{
364 				colWidth1 = menu.widgets[i]->x + menu.widgets[i]->selectedState->w;
365 			}
366 		}
367 
368 		y += menu.widgets[i]->selectedState->h + BUTTON_PADDING;
369 	}
370 
371 	y = menu.widgets[1]->y;
372 
373 	for (i=8;i<menu.widgetCount;i++)
374 	{
375 		if (menu.widgets[i]->label != NULL && menu.widgets[i]->normalState->w > maxWidth2)
376 		{
377 			maxWidth2 = menu.widgets[i]->normalState->w;
378 		}
379 	}
380 
381 	for (i=8;i<menu.widgetCount;i++)
382 	{
383 		menu.widgets[i]->y = y;
384 
385 		if (menu.widgets[i]->x != -1)
386 		{
387 			menu.widgets[i]->x = colWidth1 + BUTTON_PADDING + BORDER_PADDING;
388 		}
389 
390 		if (menu.widgets[i]->label != NULL)
391 		{
392 			menu.widgets[i]->label->y = y;
393 
394 			menu.widgets[i]->label->x = menu.widgets[i]->x + maxWidth2 + 10;
395 
396 			if (menu.widgets[i]->label->x + menu.widgets[i]->label->text->w > colWidth2)
397 			{
398 				colWidth2 = menu.widgets[i]->label->x + menu.widgets[i]->label->text->w;
399 			}
400 		}
401 
402 		else
403 		{
404 			if (menu.widgets[i]->x + menu.widgets[i]->selectedState->w > colWidth2)
405 			{
406 				colWidth2 = menu.widgets[i]->x + menu.widgets[i]->selectedState->w;
407 			}
408 		}
409 
410 		y += menu.widgets[i]->selectedState->h + BUTTON_PADDING;
411 	}
412 
413 	w = colWidth2 + BUTTON_PADDING;
414 
415 	if (menu.w != w)
416 	{
417 		if (menu.background != NULL)
418 		{
419 			destroyTexture(menu.background);
420 
421 			menu.background = NULL;
422 		}
423 
424 		menu.w = w;
425 		menu.h = y - BORDER_PADDING;
426 
427 		menu.background = addBorder(createSurface(menu.w, menu.h, FALSE), 255, 255, 255, 0, 0, 0);
428 
429 		menu.x = (SCREEN_WIDTH - menu.background->w) / 2;
430 		menu.y = (SCREEN_HEIGHT - menu.background->h) / 2;
431 	}
432 }
433 
freeControlMenu()434 void freeControlMenu()
435 {
436 	int i;
437 
438 	if (menu.widgets != NULL)
439 	{
440 		for (i=0;i<menu.widgetCount;i++)
441 		{
442 			freeWidget(menu.widgets[i]);
443 		}
444 
445 		free(menu.widgets);
446 
447 		menu.widgets = NULL;
448 	}
449 
450 	if (menu.background != NULL)
451 	{
452 		destroyTexture(menu.background);
453 
454 		menu.background = NULL;
455 	}
456 }
457 
redefineKey()458 static void redefineKey()
459 {
460 	int i, key, oldKey;
461 	char *text;
462 	Widget *w = menu.widgets[menu.index];
463 
464 	key = -2;
465 
466 	updateLabelText(w->label, "?");
467 
468 	oldKey = (*w->value);
469 
470 	flushInputs();
471 
472 	while (key == -2)
473 	{
474 		key = getSingleInput();
475 
476 		draw();
477 	}
478 
479 	if (key != -1)
480 	{
481 		(*w->value) = key;
482 	}
483 
484 	else
485 	{
486 		key = oldKey;
487 	}
488 
489 	text = getKeyValue(key);
490 
491 	updateLabelText(w->label, text);
492 
493 	for (i=0;i<menu.widgetCount;i++)
494 	{
495 		if (i == menu.index)
496 		{
497 			continue;
498 		}
499 
500 		if (menu.widgets[i]->value != NULL && *menu.widgets[i]->value == key)
501 		{
502 			*menu.widgets[i]->value = -1;
503 
504 			updateLabelText(menu.widgets[i]->label, "?");
505 		}
506 	}
507 
508 	realignGrid();
509 }
510 
lowerDeadZoneValue()511 static void lowerDeadZoneValue()
512 {
513 	char *text;
514 	Widget *w = menu.widgets[menu.index];
515 
516 	control.deadZone -= 1000;
517 
518 	if (control.deadZone < 0)
519 	{
520 		control.deadZone = 0;
521 	}
522 
523 	text = getDeadZoneValue(control.deadZone);
524 
525 	updateLabelText(w->label, text);
526 
527 	free(text);
528 }
529 
raiseDeadZoneValue()530 static void raiseDeadZoneValue()
531 {
532 	char *text;
533 	Widget *w = menu.widgets[menu.index];
534 
535 	control.deadZone += 1000;
536 
537 	if (control.deadZone > 32000)
538 	{
539 		control.deadZone = 32000;
540 	}
541 
542 	text = getDeadZoneValue(control.deadZone);
543 
544 	updateLabelText(w->label, text);
545 
546 	free(text);
547 }
548 
showOptionsMenu()549 static void showOptionsMenu()
550 {
551 	int i, j, valid;
552 
553 	valid = TRUE;
554 
555 	for (i=0;i<menu.widgetCount;i++)
556 	{
557 		if (menu.widgets[i]->value == NULL)
558 		{
559 			continue;
560 		}
561 
562 		if (*menu.widgets[i]->value == -1)
563 		{
564 			valid = FALSE;
565 
566 			break;
567 		}
568 
569 		for (j=0;j<menu.widgetCount;j++)
570 		{
571 			if (i == j || menu.widgets[j]->value == NULL)
572 			{
573 				continue;
574 			}
575 
576 			if (*menu.widgets[i]->value == *menu.widgets[j]->value)
577 			{
578 				valid = FALSE;
579 			}
580 		}
581 	}
582 
583 	if (valid == TRUE)
584 	{
585 		game.menu = initOptionsMenu();
586 
587 		game.drawMenu = &drawOptionsMenu;
588 	}
589 
590 	else
591 	{
592 		game.menu = initOKMenu(_("Please configure all controls"), &showControlMenu);
593 
594 		game.drawMenu = &drawOKMenu;
595 	}
596 }
597 
showControlMenu()598 static void showControlMenu()
599 {
600 	game.menu = initControlMenu();
601 
602 	game.drawMenu = &drawControlMenu;
603 }
604 
getDeadZoneValue(int value)605 static char *getDeadZoneValue(int value)
606 {
607 	char *text;
608 
609 	text = malloc(10);
610 
611 	if (text == NULL)
612 	{
613 		showErrorAndExit("Failed to allocate a whole 10 bytes for the dead zone label");
614 	}
615 
616 	SNPRINTF(text, 10, "%d", value);
617 
618 	return text;
619 }
620