DEADSOFTWARE

4a15ede8a4cce10306f652f8ed3a719d32ca08c7
[flatwaifu.git] / src / menu.c
1 /*
2 Copyright (C) Prikol Software 1996-1997
3 Copyright (C) Aleksey Volynskov 1996-1997
4 Copyright (C) <ARembo@gmail.com> 2011
6 This file is part of the Doom2D:Rembo project.
8 Doom2D:Rembo is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License version 2 as
10 published by the Free Software Foundation.
12 Doom2D:Rembo is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, see <http://www.gnu.org/licenses/> or
19 write to the Free Software Foundation, Inc.,
20 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
21 */
23 #include "glob.h"
24 #include "files.h"
25 #include "memory.h"
26 #include "error.h"
27 #include "sound.h"
28 #include "view.h"
29 #include "player.h"
30 #include "switch.h"
31 #include "menu.h"
32 #include "misc.h"
33 #include "render.h"
34 #include "config.h"
35 #include "game.h"
36 #include "player.h"
37 #include "sound.h"
38 #include "music.h"
39 #include "input.h"
40 #include "system.h"
42 #include <stdio.h>
43 #include <string.h>
44 #include <stdlib.h>
45 #include <assert.h>
47 #define PCOLORN 10
48 static byte pcolortab[PCOLORN] = {
49 0x18, 0x20, 0x40, 0x58, 0x60, 0x70, 0x80, 0xB0, 0xC0, 0xD0
50 };
51 static int p1color = 5;
52 static int p2color = 4;
54 byte _warp;
56 #define MAX_STACK 8
57 static struct {
58 const menu_t *m;
59 } stack[MAX_STACK];
60 static int stack_p = -1;
62 #define GM_MAX_INPUT 24
63 char ibuf[GM_MAX_INPUT];
64 byte input;
65 int icur;
66 int imax;
67 static byte cbuf[32];
68 short lastkey;
70 #define QSND_NUM 14
71 static int qsnd[QSND_NUM];
72 static snd_t *csnd1, *csnd2, *msnd1, *msnd2, *msnd3, *msnd4, *msnd5, *msnd6;
73 static snd_t *voc;
74 static int voc_ch;
76 static void GM_stop (void) {
77 if (voc != NULL) {
78 if (voc_ch) {
79 S_stop(voc_ch);
80 voc_ch = 0;
81 }
82 S_free(voc);
83 voc = NULL;
84 }
85 }
87 static int GM_say (const char nm[8]) {
88 snd_t *snd = S_load(nm);
89 if (snd) {
90 GM_stop();
91 voc = S_load(nm);
92 voc_ch = S_play(voc, 0, 255);
93 }
94 return 1;
95 }
97 int GM_init_int0 (menu_msg_t *msg, int i, int a, int b, int s) {
98 assert(msg != NULL);
99 msg->integer.i = i;
100 msg->integer.a = a;
101 msg->integer.b = b;
102 msg->integer.s = s;
103 return 1;
106 int GM_init_int (menu_msg_t *msg, int i, int a, int b, int s) {
107 assert(msg != NULL);
108 assert(a <= b);
109 assert(s >= 0);
110 return GM_init_int0(msg, min(max(i, a), b), a, b, s);
113 int GM_init_str (menu_msg_t *msg, char *str, int maxlen) {
114 assert(msg != NULL);
115 assert(str != NULL);
116 assert(maxlen >= 0);
117 msg->string.s = str;
118 msg->string.maxlen = maxlen;
119 return 1;
122 int basic_menu_handler (menu_msg_t *msg, byte type, char *title, char *say, int n, int *cur) {
123 assert(msg != NULL);
124 assert(type == GM_BIG || type == GM_SMALL);
125 assert(title != NULL);
126 assert(n >= 0);
127 assert(cur != NULL);
128 switch (msg->type) {
129 case GM_QUERY: return GM_init_int0(msg, *cur, n, n, type);
130 case GM_GETTITLE: return GM_init_str(msg, title, strlen(title));
131 case GM_ENTER: return say ? GM_say(say) : 1;
132 case GM_UP: *cur = GM_CYCLE(*cur - 1, 0, n - 1); return 1;
133 case GM_DOWN: *cur = GM_CYCLE(*cur + 1, 0, n - 1); return 1;
135 return 0;
138 int simple_menu_handler (menu_msg_t *msg, int i, int n, const simple_menu_t *m, int *cur) {
139 assert(msg != NULL);
140 assert(n >= 0);
141 assert(i >= 0 && i < n);
142 assert(m != NULL);
143 assert(cur != NULL);
144 switch (msg->type) {
145 case GM_GETENTRY: return GM_init_int0(msg, m->type == GM_SMALL ? GM_SMALL_BUTTON : GM_BUTTON, 0, 0, 0);
146 case GM_GETCAPTION: return GM_init_str(msg, m->entries[i].caption, strlen(m->entries[i].caption));
147 case GM_SELECT: return m->entries[i].submenu ? GM_push(m->entries[i].submenu) : 1;
149 return basic_menu_handler(msg, m->type, m->title, m->say, n, cur);
152 static int start_game (int twoplayers, int dm, int level) {
153 _2pl = twoplayers;
154 g_dm = dm;
155 g_map = level ? level : 1;
156 PL_reset();
157 pl1.color = pcolortab[p1color];
158 pl2.color = pcolortab[p2color];
159 G_start();
160 return GM_popall();
163 static int new_game_menu_handler (menu_msg_t *msg, const menu_t *m, void *data, int i) {
164 static int cur;
165 enum { ONEPLAYER, TWOPLAYERS, DEATHMATCH, __NUM__ };
166 static const simple_menu_t sm = {
167 GM_BIG, "New Game", "_NEWGAME",
169 { "One Player", NULL },
170 { "Two Players", NULL },
171 { "Deathmatch", NULL },
173 };
174 if (msg->type == GM_SELECT) {
175 switch (i) {
176 case ONEPLAYER: GM_say("_1PLAYER"); return start_game(0, 0, _warp);
177 case TWOPLAYERS: GM_say("_2PLAYER"); return start_game(1, 0, _warp);
178 case DEATHMATCH: GM_say("_DM"); return start_game(1, 1, _warp);
179 // GM_say("_COOP");
182 return simple_menu_handler(msg, i, __NUM__, &sm, &cur);
185 static const menu_t new_game_menu = {
186 NULL, &new_game_menu_handler
187 };
189 static int load_game_menu_handler (menu_msg_t *msg, const menu_t *m, void *data, int i) {
190 static int cur;
191 const int max_slots = 7;
192 assert(i >= 0 && i < max_slots);
193 switch (msg->type) {
194 case GM_ENTER: F_getsavnames(); break;
195 case GM_GETENTRY: return GM_init_int0(msg, GM_TEXTFIELD_BUTTON, 0, 0, 0);
196 case GM_GETSTR: return GM_init_str(msg, (char*)savname[i], 24);
197 case GM_SELECT:
198 if (savok[i]) {
199 load_game(i);
200 GM_popall();
202 return 1;
204 return basic_menu_handler(msg, GM_BIG, "Load game", "_OLDGAME", max_slots, &cur);
207 static const menu_t load_game_menu = {
208 NULL, &load_game_menu_handler
209 };
211 static int save_game_menu_handler (menu_msg_t *msg, const menu_t *m, void *data, int i) {
212 static int cur;
213 const int max_slots = 7;
214 assert(i >= 0 && i < max_slots);
215 switch (msg->type) {
216 case GM_ENTER:
217 if (g_st == GS_GAME) {
218 F_getsavnames();
219 break;
220 } else {
221 return GM_pop();
223 case GM_GETENTRY: return GM_init_int0(msg, GM_TEXTFIELD, 0, 0, 0);
224 case GM_GETSTR: return GM_init_str(msg, (char*)savname[i], 24);
225 case GM_END:
226 if (g_st == GS_GAME) {
227 assert(msg->string.maxlen >= 24);
228 F_savegame(i, msg->string.s);
230 return GM_popall();
232 return basic_menu_handler(msg, GM_BIG, "Save game", "_SAVGAME", max_slots, &cur);
235 static const menu_t save_game_menu = {
236 NULL, &save_game_menu_handler
237 };
239 static int sound_menu_handler (menu_msg_t *msg, const menu_t *m, void *data, int i) {
240 static int cur;
241 enum { VOLUME, __NUM__ };
242 static const simple_menu_t sm = {
243 GM_BIG, "Sound", NULL,
245 { "Volume", NULL },
247 };
248 if (i == VOLUME) {
249 switch (msg->type) {
250 case GM_GETENTRY: return GM_init_int0(msg, GM_SCROLLER, 0, 0, 0);
251 case GM_GETINT: return GM_init_int(msg, snd_vol, 0, 128, 8);
252 case GM_SETINT: S_volume(msg->integer.i); return 1;
255 return simple_menu_handler(msg, i, __NUM__, &sm, &cur);
258 static const menu_t sound_menu = {
259 NULL, &sound_menu_handler
260 };
262 static int music_menu_handler (menu_msg_t *msg, const menu_t *m, void *data, int i) {
263 static int cur;
264 enum { VOLUME, MUSIC, __NUM__ };
265 static const simple_menu_t sm = {
266 GM_BIG, "Music", NULL,
268 { "Volume", NULL },
269 { "Music: ", NULL },
271 };
272 if (i == VOLUME) {
273 switch (msg->type) {
274 case GM_GETENTRY: return GM_init_int0(msg, GM_SCROLLER, 0, 0, 0);
275 case GM_GETINT: return GM_init_int(msg, mus_vol, 0, 128, 8);
276 case GM_SETINT: S_volumemusic(msg->integer.i); return 1;
278 } else if (i == MUSIC) {
279 switch (msg->type) {
280 case GM_GETSTR: return GM_init_str(msg, g_music, strlen(g_music));
281 case GM_SELECT:
282 F_freemus();
283 F_nextmus(g_music);
284 F_loadmus(g_music);
285 S_startmusic(music_time * 2); // ???
286 return 1;
289 return simple_menu_handler(msg, i, __NUM__, &sm, &cur);
292 static const menu_t music_menu = {
293 NULL, &music_menu_handler
294 };
296 static int options_menu_handler (menu_msg_t *msg, const menu_t *m, void *data, int i) {
297 static int cur;
298 enum { VIDEO, SOUND, MUSIC, __NUM__ };
299 static const simple_menu_t sm = {
300 GM_BIG, "Options", NULL,
302 { "Video", NULL },
303 { "Sound", &sound_menu },
304 { "Music", &music_menu },
306 };
307 if (msg->type == GM_SELECT) {
308 if (i == VIDEO) {
309 const menu_t *mm = R_menu();
310 return mm ? GM_push(mm) : 1;
313 return simple_menu_handler(msg, i, __NUM__, &sm, &cur);
316 static const menu_t options_menu = {
317 NULL, &options_menu_handler
318 };
320 static int exit_menu_handler (menu_msg_t *msg, const menu_t *m, void *data, int i) {
321 static int cur;
322 enum { YES, NO, __NUM__ };
323 static const simple_menu_t sm = {
324 GM_SMALL, "You are sure?", NULL,
326 { "Yes", NULL },
327 { "No", NULL },
329 };
330 if (msg->type == GM_ENTER) {
331 return GM_say(rand() & 1 ? "_EXIT1" : "_EXIT2");
332 } else if (msg->type == GM_SELECT) {
333 switch (i) {
334 case YES:
335 F_freemus();
336 GM_stop();
337 Z_sound(S_get(qsnd[myrand(QSND_NUM)]), 255);
338 S_wait();
339 ERR_quit();
340 return 1;
341 case NO:
342 return GM_pop();
345 return simple_menu_handler(msg, i, __NUM__, &sm, &cur);
348 static const menu_t exit_menu = {
349 NULL, &exit_menu_handler
350 };
352 static int main_menu_handler (menu_msg_t *msg, const menu_t *m, void *data, int i) {
353 static int cur;
354 enum { NEWGAME, OLDGAME, SAVEGAME, OPTIONS, EXIT, __NUM__ };
355 static const simple_menu_t sm = {
356 GM_BIG, "Menu", NULL,
358 { "New Game", &new_game_menu },
359 { "Load Game", &load_game_menu },
360 { "Save Game", &save_game_menu },
361 { "Options", &options_menu },
362 { "Exit", &exit_menu },
364 };
365 return simple_menu_handler(msg, i, __NUM__, &sm, &cur);
368 static const menu_t main_menu = {
369 NULL, &main_menu_handler
370 };
372 int GM_push (const menu_t *m) {
373 assert(m != NULL);
374 assert(stack_p >= -1);
375 assert(stack_p < MAX_STACK - 1);
376 menu_msg_t msg;
377 stack_p += 1;
378 stack[stack_p].m = m;
379 msg.type = GM_ENTER;
380 GM_send_this(m, &msg);
381 return 1;
384 int GM_pop (void) {
385 assert(stack_p >= 0);
386 menu_msg_t msg;
387 stack_p -= 1;
388 msg.type = GM_LEAVE;
389 GM_send_this(stack[stack_p + 1].m, &msg);
390 return 1;
393 int GM_popall (void) {
394 int i;
395 for (i = 0; i >= -1; i--) {
396 GM_pop();
398 return 1;
401 const menu_t *GM_get (void) {
402 if (stack_p >= 0) {
403 return stack[stack_p].m;
404 } else {
405 return NULL;
409 static void GM_normalize_message (menu_msg_t *msg) {
410 switch (msg->type) {
411 case GM_SETINT:
412 msg->integer.i = min(max(msg->integer.i, msg->integer.a), msg->integer.b);
413 break;
414 case GM_SETSTR:
415 assert(msg->string.maxlen >= 0);
416 break;
420 int GM_send_this (const menu_t *m, menu_msg_t *msg) {
421 assert(m != NULL);
422 assert(msg != NULL);
423 if (m->handler != NULL) {
424 GM_normalize_message(msg);
425 return m->handler(msg, m, m->data, 0);
427 return 0;
430 int GM_send (const menu_t *m, int i, menu_msg_t *msg) {
431 assert(m != NULL);
432 assert(i >= 0);
433 assert(msg != NULL);
434 if (m->handler != NULL) {
435 GM_normalize_message(msg);
436 return m->handler(msg, m, m->data, i);
438 return 0;
441 void G_code (void) {
442 void *s;
443 s=csnd2;
444 if(memcmp(cbuf+32-5,"IDDQD",5)==0) {
445 PL_hit(&pl1,400,0,HIT_SOME);
446 if(_2pl) PL_hit(&pl2,400,0,HIT_SOME);
447 s=csnd1;
448 }else if(memcmp(cbuf+32-4,"TANK",4)==0) {
449 pl1.life=pl1.armor=200;pl1.drawst|=PL_DRAWARMOR|PL_DRAWLIFE;
450 if(_2pl) {pl2.life=pl2.armor=200;pl2.drawst|=PL_DRAWARMOR|PL_DRAWLIFE;}
451 }else if(memcmp(cbuf+32-8,"BULLFROG",8)==0) {
452 PL_JUMP=(PL_JUMP==10)?20:10;
453 }else if(memcmp(cbuf+32-8,"FORMULA1",8)==0) {
454 PL_RUN=(PL_RUN==8)?24:8;
455 }else if(memcmp(cbuf+32-5,"RAMBO",5)==0) {
456 pl1.ammo=pl1.shel=pl1.rock=pl1.cell=pl1.fuel=30000;
457 pl1.wpns=0x7FF;pl1.drawst|=PL_DRAWWPN|PL_DRAWKEYS;
458 pl1.keys=0x70;
459 if(_2pl) {
460 pl2.ammo=pl2.shel=pl2.rock=pl2.cell=pl1.fuel=30000;
461 pl2.wpns=0x7FF;pl2.drawst|=PL_DRAWWPN|PL_DRAWKEYS;
462 pl2.keys=0x70;
464 }else if(memcmp(cbuf+32-5,"UJHTW",5)==0) {
465 p_immortal=!p_immortal;
466 }else if(memcmp(cbuf+32-9,",TKSQJHTK",9)==0) {
467 p_fly=!p_fly;
468 }else if(memcmp(cbuf+32-6,"CBVCBV",6)==0) {
469 SW_cheat_open();
470 }else if(memcmp(cbuf+32-7,"GOODBYE",7)==0) {
471 g_exit=1;
472 }else if(memcmp(cbuf+32-9,"GJITKYF",7)==0) {
473 if(cbuf[30]>='0' && cbuf[30]<='9' && cbuf[31]>='0' && cbuf[31]<='9') {
474 g_map=(cbuf[30]=='0')?0:(cbuf[30]-'0')*10;
475 g_map+=(cbuf[31]=='0')?0:(cbuf[31]-'0');
476 G_start();
478 }else return;
479 memset(cbuf,0,32);
480 Z_sound(s,128);
483 static int strnlen (const char *s, int len) {
484 int i = 0;
485 while (i < len && s[i] != 0) {
486 i++;
488 return i;
491 static int state_for_anykey (int x) {
492 return x == GS_TITLE || x == GS_ENDSCR;
495 int GM_act (void) {
496 menu_msg_t msg;
497 int n, cur, type;
498 const menu_t *m = GM_get ();
499 if (m == NULL) {
500 if (lastkey == KEY_ESCAPE || (state_for_anykey(g_st) && lastkey != KEY_UNKNOWN)) {
501 GM_push(&main_menu);
502 Z_sound(msnd3, 128);
504 } else {
505 msg.type = GM_QUERY;
506 assert(GM_send_this(m, &msg));
507 cur = msg.integer.i;
508 n = msg.integer.a;
509 msg.type = GM_GETENTRY;
510 assert(GM_send(m, cur, &msg));
511 type = msg.integer.i;
512 switch (lastkey) {
513 case KEY_ESCAPE:
514 if (type == GM_TEXTFIELD && input) {
515 input = 0;
516 Y_disable_text_input();
517 msg.type = GM_CANCEL;
518 GM_send(m, cur, &msg);
519 } else {
520 GM_pop();
521 Z_sound(msnd4, 128);
523 break;
524 case KEY_UP:
525 case KEY_DOWN:
526 msg.type = lastkey == KEY_UP ? GM_UP : GM_DOWN;
527 if (GM_send(m, cur, &msg)) {
528 Z_sound(msnd1, 128);
530 break;
531 case KEY_LEFT:
532 case KEY_RIGHT:
533 if (type == GM_SCROLLER) {
534 msg.integer.type = GM_GETINT;
535 if (GM_send(m, cur, &msg)) {
536 msg.integer.type = GM_SETINT;
537 msg.integer.i += lastkey == KEY_LEFT ? -msg.integer.s : msg.integer.s;
538 msg.integer.i = min(max(msg.integer.i, msg.integer.a), msg.integer.b);
539 if (GM_send(m, cur, &msg)) {
540 Z_sound(lastkey == KEY_LEFT ? msnd5 : msnd6, 255);
543 } else if (type == GM_TEXTFIELD) {
544 //if (input) {
545 // icur += lastkey == KEY_LEFT ? -1 : +1;
546 // icur = min(max(icur, 0), strnlen(ibuf, imax));
547 //}
549 break;
550 case KEY_BACKSPACE:
551 if (type == GM_TEXTFIELD) {
552 if (input && icur > 0) {
553 // FIXIT buffers in strncpy must not overlap
554 strncpy(&ibuf[icur - 1], &ibuf[icur], imax - icur);
555 ibuf[imax - 1] = 0;
556 icur -= 1;
559 break;
560 case KEY_RETURN:
561 if (type == GM_TEXTFIELD) {
562 if (input) {
563 input = 0;
564 Y_disable_text_input();
565 msg.type = GM_END;
566 msg.string.s = ibuf;
567 msg.string.maxlen = imax;
568 GM_send(m, cur, &msg);
569 } else {
570 msg.type = GM_GETSTR;
571 if (GM_send(m, cur, &msg)) {
572 imax = min(msg.string.maxlen, GM_MAX_INPUT);
573 strncpy(ibuf, msg.string.s, imax);
574 icur = strnlen(ibuf, imax);
575 } else {
576 memset(ibuf, 0, GM_MAX_INPUT);
577 imax = GM_MAX_INPUT;
578 icur = 0;
580 input = 1;
581 Y_enable_text_input();
582 msg.type = GM_BEGIN;
583 GM_send(m, cur, &msg);
585 Z_sound(msnd2, 128);
586 } else {
587 msg.type = GM_SELECT;
588 if (GM_send(m, cur, &msg)) {
589 Z_sound(msnd2, 128);
592 break;
595 lastkey = KEY_UNKNOWN;
596 return m != NULL;
599 void GM_input (int ch) {
600 if (ch != 0 && input) {
601 if (icur < imax) {
602 ibuf[icur] = ch;
603 icur += 1;
604 if (icur < imax) {
605 ibuf[icur] = 0;
611 void GM_key (int key, int down) {
612 int i;
613 if (down) {
614 lastkey = key;
615 if (!_2pl || cheat) {
616 for (i = 0; i < 31; i++) {
617 cbuf[i] = cbuf[i + 1];
619 if (key >= KEY_0 && key <= KEY_9) {
620 cbuf[31] = key - KEY_0 + '0';
621 } else if (key >= KEY_A && key <= KEY_Z) {
622 cbuf[31] = key - KEY_A + 'A';
623 } else {
624 cbuf[31] = 0;
630 void GM_init (void) {
631 int i;
632 char s[8];
633 static const char nm[QSND_NUM][6] = {
634 "CYBSIT", "KNTDTH", "MNPAIN", "PEPAIN", "SLOP", "MANSIT", "BOSPN", "VILACT",
635 "PLFALL", "BGACT", "BGDTH2", "POPAIN", "SGTATK", "VILDTH"
636 };
637 s[0] = 'D';
638 s[1] = 'S';
639 for (i = 0; i < QSND_NUM; ++i) {
640 memcpy(s + 2, nm[i], 6);
641 qsnd[i] = F_getresid(s);
643 csnd1 = Z_getsnd("HAHA1");
644 csnd2 = Z_getsnd("RADIO");
645 msnd1 = Z_getsnd("PSTOP");
646 msnd2 = Z_getsnd("PISTOL");
647 msnd3 = Z_getsnd("SWTCHN");
648 msnd4 = Z_getsnd("SWTCHX");
649 msnd5 = Z_getsnd("SUDI");
650 msnd6 = Z_getsnd("TUDI");
651 F_loadmus("MENU");
652 S_startmusic(0);