DEADSOFTWARE

7286a45375a11e4a271996357ad3b9150b93f9d6
[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 "save.h"
44 #include <stdio.h>
45 #include <string.h>
46 #include <stdlib.h>
47 #include <assert.h>
49 #define PCOLORN 10
50 static byte pcolortab[PCOLORN] = {
51 0x18, 0x20, 0x40, 0x58, 0x60, 0x70, 0x80, 0xB0, 0xC0, 0xD0
52 };
53 static int p1color = 5;
54 static int p2color = 4;
56 byte _warp;
58 #define MAX_STACK 8
59 static struct {
60 const menu_t *m;
61 } stack[MAX_STACK];
62 static int stack_p = -1;
64 #define GM_MAX_INPUT 24
65 char ibuf[GM_MAX_INPUT];
66 byte input;
67 int icur;
68 int imax;
69 static byte cbuf[32];
70 short lastkey;
72 #define QSND_NUM 14
73 static int qsnd[QSND_NUM];
74 static snd_t *csnd1, *csnd2, *msnd1, *msnd2, *msnd3, *msnd4, *msnd5, *msnd6;
75 static snd_t *voc;
76 static int voc_ch;
78 static void GM_stop (void) {
79 if (voc != NULL) {
80 if (voc_ch) {
81 S_stop(voc_ch);
82 voc_ch = 0;
83 }
84 S_free(voc);
85 voc = NULL;
86 }
87 }
89 static int GM_say (const char nm[8]) {
90 snd_t *snd = S_load(nm);
91 if (snd) {
92 GM_stop();
93 voc = S_load(nm);
94 voc_ch = S_play(voc, 0, 255);
95 }
96 return 1;
97 }
99 int GM_init_int0 (menu_msg_t *msg, int i, int a, int b, int s) {
100 assert(msg != NULL);
101 msg->integer.i = i;
102 msg->integer.a = a;
103 msg->integer.b = b;
104 msg->integer.s = s;
105 return 1;
108 int GM_init_int (menu_msg_t *msg, int i, int a, int b, int s) {
109 assert(msg != NULL);
110 assert(a <= b);
111 assert(s >= 0);
112 return GM_init_int0(msg, min(max(i, a), b), a, b, s);
115 int GM_init_str (menu_msg_t *msg, char *str, int maxlen) {
116 assert(msg != NULL);
117 assert(str != NULL);
118 assert(maxlen >= 0);
119 msg->string.s = str;
120 msg->string.maxlen = maxlen;
121 return 1;
124 int basic_menu_handler (menu_msg_t *msg, byte type, char *title, char *say, int n, int *cur) {
125 assert(msg != NULL);
126 assert(type == GM_BIG || type == GM_SMALL);
127 assert(title != NULL);
128 assert(n >= 0);
129 assert(cur != NULL);
130 switch (msg->type) {
131 case GM_QUERY: return GM_init_int0(msg, *cur, n, n, type);
132 case GM_GETTITLE: return GM_init_str(msg, title, strlen(title));
133 case GM_ENTER: return say ? GM_say(say) : 1;
134 case GM_UP: *cur = GM_CYCLE(*cur - 1, 0, n - 1); return 1;
135 case GM_DOWN: *cur = GM_CYCLE(*cur + 1, 0, n - 1); return 1;
137 return 0;
140 int simple_menu_handler (menu_msg_t *msg, int i, int n, const simple_menu_t *m, int *cur) {
141 assert(msg != NULL);
142 assert(n >= 0);
143 assert(i >= 0 && i < n);
144 assert(m != NULL);
145 assert(cur != NULL);
146 switch (msg->type) {
147 case GM_GETENTRY: return GM_init_int0(msg, m->type == GM_SMALL ? GM_SMALL_BUTTON : GM_BUTTON, 0, 0, 0);
148 case GM_GETCAPTION: return GM_init_str(msg, m->entries[i].caption, strlen(m->entries[i].caption));
149 case GM_SELECT: return m->entries[i].submenu ? GM_push(m->entries[i].submenu) : 1;
151 return basic_menu_handler(msg, m->type, m->title, m->say, n, cur);
154 static int start_game (int twoplayers, int dm, int level) {
155 _2pl = twoplayers;
156 g_dm = dm;
157 g_map = level ? level : 1;
158 PL_reset();
159 pl1.color = pcolortab[p1color];
160 pl2.color = pcolortab[p2color];
161 G_start();
162 return GM_popall();
165 static int new_game_menu_handler (menu_msg_t *msg, const menu_t *m, void *data, int i) {
166 static int cur;
167 enum { ONEPLAYER, TWOPLAYERS, DEATHMATCH, __NUM__ };
168 static const simple_menu_t sm = {
169 GM_BIG, "New Game", "_NEWGAME",
171 { "One Player", NULL },
172 { "Two Players", NULL },
173 { "Deathmatch", NULL },
175 };
176 if (msg->type == GM_SELECT) {
177 switch (i) {
178 case ONEPLAYER: GM_say("_1PLAYER"); return start_game(0, 0, _warp);
179 case TWOPLAYERS: GM_say("_2PLAYER"); return start_game(1, 0, _warp);
180 case DEATHMATCH: GM_say("_DM"); return start_game(1, 1, _warp);
181 // GM_say("_COOP");
184 return simple_menu_handler(msg, i, __NUM__, &sm, &cur);
187 static const menu_t new_game_menu = {
188 NULL, &new_game_menu_handler
189 };
191 static int load_game_menu_handler (menu_msg_t *msg, const menu_t *m, void *data, int i) {
192 static int cur;
193 const int max_slots = 7;
194 assert(i >= 0 && i < max_slots);
195 switch (msg->type) {
196 case GM_ENTER: F_getsavnames(); break;
197 case GM_GETENTRY: return GM_init_int0(msg, GM_TEXTFIELD_BUTTON, 0, 0, 0);
198 case GM_GETSTR: return GM_init_str(msg, (char*)savname[i], 24);
199 case GM_SELECT:
200 if (savok[i]) {
201 load_game(i);
202 GM_popall();
204 return 1;
206 return basic_menu_handler(msg, GM_BIG, "Load game", "_OLDGAME", max_slots, &cur);
209 static const menu_t load_game_menu = {
210 NULL, &load_game_menu_handler
211 };
213 static int save_game_menu_handler (menu_msg_t *msg, const menu_t *m, void *data, int i) {
214 static int cur;
215 const int max_slots = 7;
216 assert(i >= 0 && i < max_slots);
217 switch (msg->type) {
218 case GM_ENTER:
219 if (g_st == GS_GAME) {
220 F_getsavnames();
221 break;
222 } else {
223 return GM_pop();
225 case GM_GETENTRY: return GM_init_int0(msg, GM_TEXTFIELD, 0, 0, 0);
226 case GM_GETSTR: return GM_init_str(msg, (char*)savname[i], 24);
227 case GM_END:
228 if (g_st == GS_GAME) {
229 assert(msg->string.maxlen >= 24);
230 F_savegame(i, msg->string.s);
232 return GM_popall();
234 return basic_menu_handler(msg, GM_BIG, "Save game", "_SAVGAME", max_slots, &cur);
237 static const menu_t save_game_menu = {
238 NULL, &save_game_menu_handler
239 };
241 static int options_menu_handler (menu_msg_t *msg, const menu_t *m, void *data, int i) {
242 static int cur;
243 const menu_t *mm;
244 enum { VIDEO, SOUND, MUSIC, __NUM__ };
245 static const simple_menu_t sm = {
246 GM_BIG, "Options", NULL,
248 { "Video", NULL },
249 { "Sound", NULL },
250 { "Music", NULL },
252 };
253 if (msg->type == GM_SELECT) {
254 switch (i) {
255 case VIDEO: mm = R_menu(); break;
256 case SOUND: mm = S_menu(); break;
257 case MUSIC: mm = MUS_menu(); break;
258 default: mm = NULL;
260 if (mm != NULL) {
261 return GM_push(mm);
264 return simple_menu_handler(msg, i, __NUM__, &sm, &cur);
267 static const menu_t options_menu = {
268 NULL, &options_menu_handler
269 };
271 static int exit_menu_handler (menu_msg_t *msg, const menu_t *m, void *data, int i) {
272 static int cur;
273 enum { YES, NO, __NUM__ };
274 static const simple_menu_t sm = {
275 GM_SMALL, "You are sure?", NULL,
277 { "Yes", NULL },
278 { "No", NULL },
280 };
281 if (msg->type == GM_ENTER) {
282 return GM_say(rand() & 1 ? "_EXIT1" : "_EXIT2");
283 } else if (msg->type == GM_SELECT) {
284 switch (i) {
285 case YES:
286 MUS_free();
287 GM_stop();
288 Z_sound(S_get(qsnd[myrand(QSND_NUM)]), 255);
289 S_wait();
290 ERR_quit();
291 return 1;
292 case NO:
293 return GM_pop();
296 return simple_menu_handler(msg, i, __NUM__, &sm, &cur);
299 static const menu_t exit_menu = {
300 NULL, &exit_menu_handler
301 };
303 static int main_menu_handler (menu_msg_t *msg, const menu_t *m, void *data, int i) {
304 static int cur;
305 enum { NEWGAME, OLDGAME, SAVEGAME, OPTIONS, EXIT, __NUM__ };
306 assert(i >= 0 && i < __NUM__);
307 static const simple_menu_t sm = {
308 GM_BIG, "Menu", NULL,
310 { "New Game", &new_game_menu },
311 { "Load Game", &load_game_menu },
312 { "Save Game", &save_game_menu },
313 { "Options", &options_menu },
314 { "Exit", &exit_menu },
316 };
317 return simple_menu_handler(msg, i, __NUM__, &sm, &cur);
320 static const menu_t main_menu = {
321 NULL, &main_menu_handler
322 };
324 int GM_push (const menu_t *m) {
325 assert(m != NULL);
326 assert(stack_p >= -1);
327 assert(stack_p < MAX_STACK - 1);
328 menu_msg_t msg;
329 stack_p += 1;
330 stack[stack_p].m = m;
331 msg.type = GM_ENTER;
332 GM_send_this(m, &msg);
333 return 1;
336 int GM_pop (void) {
337 assert(stack_p >= 0);
338 menu_msg_t msg;
339 stack_p -= 1;
340 msg.type = GM_LEAVE;
341 GM_send_this(stack[stack_p + 1].m, &msg);
342 return 1;
345 int GM_popall (void) {
346 int i;
347 for (i = 0; i >= -1; i--) {
348 GM_pop();
350 return 1;
353 const menu_t *GM_get (void) {
354 if (stack_p >= 0) {
355 return stack[stack_p].m;
356 } else {
357 return NULL;
361 static void GM_normalize_message (menu_msg_t *msg) {
362 switch (msg->type) {
363 case GM_SETINT:
364 msg->integer.i = min(max(msg->integer.i, msg->integer.a), msg->integer.b);
365 break;
366 case GM_SETSTR:
367 assert(msg->string.maxlen >= 0);
368 break;
372 int GM_send_this (const menu_t *m, menu_msg_t *msg) {
373 assert(m != NULL);
374 assert(msg != NULL);
375 if (m->handler != NULL) {
376 GM_normalize_message(msg);
377 return m->handler(msg, m, m->data, 0);
379 return 0;
382 int GM_send (const menu_t *m, int i, menu_msg_t *msg) {
383 assert(m != NULL);
384 assert(i >= 0);
385 assert(msg != NULL);
386 if (m->handler != NULL) {
387 GM_normalize_message(msg);
388 return m->handler(msg, m, m->data, i);
390 return 0;
393 void G_code (void) {
394 void *s;
395 s=csnd2;
396 if(memcmp(cbuf+32-5,"IDDQD",5)==0) {
397 PL_hit(&pl1,400,0,HIT_SOME);
398 if(_2pl) PL_hit(&pl2,400,0,HIT_SOME);
399 s=csnd1;
400 }else if(memcmp(cbuf+32-4,"TANK",4)==0) {
401 pl1.life=pl1.armor=200;pl1.drawst|=PL_DRAWARMOR|PL_DRAWLIFE;
402 if(_2pl) {pl2.life=pl2.armor=200;pl2.drawst|=PL_DRAWARMOR|PL_DRAWLIFE;}
403 }else if(memcmp(cbuf+32-8,"BULLFROG",8)==0) {
404 PL_JUMP=(PL_JUMP==10)?20:10;
405 }else if(memcmp(cbuf+32-8,"FORMULA1",8)==0) {
406 PL_RUN=(PL_RUN==8)?24:8;
407 }else if(memcmp(cbuf+32-5,"RAMBO",5)==0) {
408 pl1.ammo=pl1.shel=pl1.rock=pl1.cell=pl1.fuel=30000;
409 pl1.wpns=0x7FF;pl1.drawst|=PL_DRAWWPN|PL_DRAWKEYS;
410 pl1.keys=0x70;
411 if(_2pl) {
412 pl2.ammo=pl2.shel=pl2.rock=pl2.cell=pl1.fuel=30000;
413 pl2.wpns=0x7FF;pl2.drawst|=PL_DRAWWPN|PL_DRAWKEYS;
414 pl2.keys=0x70;
416 }else if(memcmp(cbuf+32-5,"UJHTW",5)==0) {
417 p_immortal=!p_immortal;
418 }else if(memcmp(cbuf+32-9,",TKSQJHTK",9)==0) {
419 p_fly=!p_fly;
420 }else if(memcmp(cbuf+32-6,"CBVCBV",6)==0) {
421 SW_cheat_open();
422 }else if(memcmp(cbuf+32-7,"GOODBYE",7)==0) {
423 g_exit=1;
424 }else if(memcmp(cbuf+32-9,"GJITKYF",7)==0) {
425 if(cbuf[30]>='0' && cbuf[30]<='9' && cbuf[31]>='0' && cbuf[31]<='9') {
426 g_map=(cbuf[30]=='0')?0:(cbuf[30]-'0')*10;
427 g_map+=(cbuf[31]=='0')?0:(cbuf[31]-'0');
428 G_start();
430 }else return;
431 memset(cbuf,0,32);
432 Z_sound(s,128);
435 static int strnlen (const char *s, int len) {
436 int i = 0;
437 while (i < len && s[i] != 0) {
438 i++;
440 return i;
443 static int state_for_anykey (int x) {
444 return x == GS_TITLE || x == GS_ENDSCR;
447 int GM_act (void) {
448 menu_msg_t msg;
449 int n, cur, type;
450 const menu_t *m = GM_get();
451 if (m == NULL) {
452 if (lastkey == KEY_ESCAPE || (state_for_anykey(g_st) && lastkey != KEY_UNKNOWN)) {
453 GM_push(&main_menu);
454 Z_sound(msnd3, 128);
456 } else {
457 msg.type = GM_QUERY;
458 if (GM_send_this(m, &msg)) {
459 cur = msg.integer.i;
460 n = msg.integer.a;
461 msg.type = GM_GETENTRY;
462 if (GM_send(m, cur, &msg)) {
463 type = msg.integer.i;
464 switch (lastkey) {
465 case KEY_ESCAPE:
466 if (type == GM_TEXTFIELD && input) {
467 input = 0;
468 Y_disable_text_input();
469 msg.type = GM_CANCEL;
470 GM_send(m, cur, &msg);
471 } else {
472 GM_pop();
473 Z_sound(msnd4, 128);
475 break;
476 case KEY_UP:
477 case KEY_DOWN:
478 if (input == 0) {
479 msg.type = lastkey == KEY_UP ? GM_UP : GM_DOWN;
480 if (GM_send(m, cur, &msg)) {
481 Z_sound(msnd1, 128);
484 break;
485 case KEY_LEFT:
486 case KEY_RIGHT:
487 if (type == GM_SCROLLER) {
488 msg.integer.type = GM_GETINT;
489 if (GM_send(m, cur, &msg)) {
490 msg.integer.type = GM_SETINT;
491 msg.integer.i += lastkey == KEY_LEFT ? -msg.integer.s : msg.integer.s;
492 msg.integer.i = min(max(msg.integer.i, msg.integer.a), msg.integer.b);
493 if (GM_send(m, cur, &msg)) {
494 Z_sound(lastkey == KEY_LEFT ? msnd5 : msnd6, 255);
497 } else if (type == GM_TEXTFIELD) {
498 //if (input) {
499 // icur += lastkey == KEY_LEFT ? -1 : +1;
500 // icur = min(max(icur, 0), strnlen(ibuf, imax));
501 //}
503 break;
504 case KEY_BACKSPACE:
505 if (type == GM_TEXTFIELD) {
506 if (input && icur > 0) {
507 // FIXIT buffers in strncpy must not overlap
508 strncpy(&ibuf[icur - 1], &ibuf[icur], imax - icur);
509 ibuf[imax - 1] = 0;
510 icur -= 1;
513 break;
514 case KEY_RETURN:
515 if (type == GM_TEXTFIELD) {
516 if (input) {
517 input = 0;
518 Y_disable_text_input();
519 msg.type = GM_END;
520 msg.string.s = ibuf;
521 msg.string.maxlen = imax;
522 GM_send(m, cur, &msg);
523 } else {
524 msg.type = GM_GETSTR;
525 if (GM_send(m, cur, &msg)) {
526 imax = min(msg.string.maxlen, GM_MAX_INPUT);
527 strncpy(ibuf, msg.string.s, imax);
528 icur = strnlen(ibuf, imax);
529 } else {
530 memset(ibuf, 0, GM_MAX_INPUT);
531 imax = GM_MAX_INPUT;
532 icur = 0;
534 input = 1;
535 Y_enable_text_input();
536 msg.type = GM_BEGIN;
537 GM_send(m, cur, &msg);
539 Z_sound(msnd2, 128);
540 } else {
541 msg.type = GM_SELECT;
542 if (cur < 0) abort();
543 if (GM_send(m, cur, &msg)) {
544 Z_sound(msnd2, 128);
547 break;
552 lastkey = KEY_UNKNOWN;
553 return m != NULL;
556 void GM_input (int ch) {
557 if (ch != 0 && input) {
558 if (icur < imax) {
559 ibuf[icur] = ch;
560 icur += 1;
561 if (icur < imax) {
562 ibuf[icur] = 0;
568 void GM_key (int key, int down) {
569 int i;
570 if (down) {
571 lastkey = key;
572 if (!_2pl || cheat) {
573 for (i = 0; i < 31; i++) {
574 cbuf[i] = cbuf[i + 1];
576 if (key >= KEY_0 && key <= KEY_9) {
577 cbuf[31] = key - KEY_0 + '0';
578 } else if (key >= KEY_A && key <= KEY_Z) {
579 cbuf[31] = key - KEY_A + 'A';
580 } else {
581 cbuf[31] = 0;
587 void GM_init (void) {
588 int i;
589 char s[8];
590 static const char nm[QSND_NUM][6] = {
591 "CYBSIT", "KNTDTH", "MNPAIN", "PEPAIN", "SLOP", "MANSIT", "BOSPN", "VILACT",
592 "PLFALL", "BGACT", "BGDTH2", "POPAIN", "SGTATK", "VILDTH"
593 };
594 s[0] = 'D';
595 s[1] = 'S';
596 for (i = 0; i < QSND_NUM; ++i) {
597 memcpy(s + 2, nm[i], 6);
598 qsnd[i] = F_getresid(s);
600 csnd1 = Z_getsnd("HAHA1");
601 csnd2 = Z_getsnd("RADIO");
602 msnd1 = Z_getsnd("PSTOP");
603 msnd2 = Z_getsnd("PISTOL");
604 msnd3 = Z_getsnd("SWTCHN");
605 msnd4 = Z_getsnd("SWTCHX");
606 msnd5 = Z_getsnd("SUDI");
607 msnd6 = Z_getsnd("TUDI");
608 MUS_load("MENU");
609 MUS_start(0);