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