1 /*
2 * Alizarin Tetris
3 * The game menu file.
4 *
5 * Copyright 2000, Kiri Wagstaff & Westley Weimer
6 */
7
8 #include <config.h> /* go autoconf! */
9 #include <unistd.h>
10 #include <sys/types.h>
11
12 /* header files */
13 #include "atris.h"
14 #include "ai.h"
15 #include "display.h"
16 #include "grid.h"
17 #include "piece.h"
18 #include "button.h"
19 #include "sound.h"
20 #include "menu.h"
21 #include "options.h"
22
23 /* function prototypes */
24 #include "ai.pro"
25 #include "event.pro"
26 #include "piece.pro"
27 #include "button.pro"
28 #include "menu.pro"
29 #include "display.pro"
30
31 typedef enum {
32 ColorStyleMenu = 0,
33 SoundStyleMenu = 1,
34 PieceStyleMenu = 2,
35 GameMenu = 3,
36 OptionsMenu = 4,
37 MainMenu = 5,
38 } MainMenuChoice;
39
40 typedef enum {
41 Opt_ToggleFullScreen,
42 Opt_ToggleFlame,
43 Opt_ToggleSpecial,
44 Opt_FasterLevels,
45 Opt_LongSettleDelay,
46 Opt_UpwardRotation,
47 Opt_KeyRepeat,
48 } OptionMenuChoice;
49
50 #define MAX_MENU_CHOICE 6
51
52 static int start_playing = 0;
53 static sound_styles *_ss;
54 static color_styles *_cs;
55 static piece_styles *_ps;
56 static WalkRadioGroup *wrg = NULL;
57
58 static GT _local_gametype;
59
60 /***************************************************************************/
61 /* Update the given choice on the main menu */
updateMenu(int whichSubMenu,int choice)62 static void updateMenu(int whichSubMenu, int choice)
63 {
64 wrg->wr[MainMenu].label[whichSubMenu] = wrg->wr[whichSubMenu].label[choice];
65 clear_radio(&wrg->wr[MainMenu]);
66 setup_radio(&wrg->wr[MainMenu]);
67 wrg->wr[MainMenu].x = (screen->w/3 - wrg->wr[MainMenu].area.w)/2;
68 wrg->wr[MainMenu].y = (screen->h - wrg->wr[MainMenu].area.h)/2;
69 }
70
71 char key_repeat_label[] = "Key Repeat: 16 " ;
72
OptionsMenu_setup()73 static void OptionsMenu_setup()
74 {
75 wrg->wr[OptionsMenu].label[Opt_ToggleFullScreen] = "Toggle Full Screen";
76 if (Options.flame_wanted)
77 wrg->wr[OptionsMenu].label[Opt_ToggleFlame] = "Background Flame: On";
78 else
79 wrg->wr[OptionsMenu].label[Opt_ToggleFlame] = "Background Flame: Off";
80 if (Options.special_wanted)
81 wrg->wr[OptionsMenu].label[Opt_ToggleSpecial] = "Power Pieces: On";
82 else
83 wrg->wr[OptionsMenu].label[Opt_ToggleSpecial] = "Power Pieces: Off";
84 if (Options.faster_levels)
85 wrg->wr[OptionsMenu].label[Opt_FasterLevels] = "Double Difficulty: On";
86 else
87 wrg->wr[OptionsMenu].label[Opt_FasterLevels] = "Double Difficulty: Off";
88 if (Options.long_settle_delay)
89 wrg->wr[OptionsMenu].label[Opt_LongSettleDelay] = "Long Settle Delay: On";
90 else
91 wrg->wr[OptionsMenu].label[Opt_LongSettleDelay] = "Long Settle Delay: Off";
92 if (Options.upward_rotation)
93 wrg->wr[OptionsMenu].label[Opt_UpwardRotation] = "Upward Rotation: On";
94 else
95 wrg->wr[OptionsMenu].label[Opt_UpwardRotation] = "Upward Rotation: Off";
96
97 SPRINTF(key_repeat_label,"Key Repeat: %.2d", Options.key_repeat_delay);
98 wrg->wr[OptionsMenu].label[Opt_KeyRepeat] = key_repeat_label;
99 }
100
OptionsMenu_action(WalkRadio * wr)101 static int OptionsMenu_action(WalkRadio *wr)
102 {
103 switch (wr->defaultchoice) {
104 case Opt_ToggleFullScreen:
105 SDL_WM_ToggleFullScreen(screen);
106 break;
107 case Opt_ToggleFlame: Options.flame_wanted = ! Options.flame_wanted; break;
108 case Opt_ToggleSpecial: Options.special_wanted = ! Options.special_wanted; break;
109 case Opt_FasterLevels: Options.faster_levels = ! Options.faster_levels; break;
110 case Opt_LongSettleDelay: Options.long_settle_delay = ! Options.long_settle_delay; break;
111 case Opt_UpwardRotation: Options.upward_rotation = ! Options.upward_rotation; break;
112 case Opt_KeyRepeat: Options.key_repeat_delay = pick_key_repeat(screen); break;
113 default:
114 break;
115 }
116 OptionsMenu_setup();
117 clear_radio(&wrg->wr[OptionsMenu]);
118 setup_radio(&wrg->wr[OptionsMenu]);
119 return 0;
120 }
121
ColorStyleMenu_action(WalkRadio * wr)122 static int ColorStyleMenu_action(WalkRadio *wr)
123 {
124 _cs->choice = wr->defaultchoice;
125 /* Update this choice on the main menu */
126 updateMenu((int)ColorStyleMenu, wr->defaultchoice);
127 return 1;
128 }
SoundStyleMenu_action(WalkRadio * wr)129 static int SoundStyleMenu_action(WalkRadio *wr)
130 {
131 _ss->choice = wr->defaultchoice;
132 play_all_sounds(_ss->style[_ss->choice]);
133 /* Update this choice on the main menu */
134 updateMenu((int)SoundStyleMenu, wr->defaultchoice);
135 return 1;
136 }
PieceStyleMenu_action(WalkRadio * wr)137 static int PieceStyleMenu_action(WalkRadio *wr)
138 {
139 _ps->choice = wr->defaultchoice;
140 /* Update this choice on the main menu */
141 updateMenu((int)PieceStyleMenu, wr->defaultchoice);
142 return 1;
143 }
GameMenu_action(WalkRadio * wr)144 static int GameMenu_action(WalkRadio *wr)
145 {
146 _local_gametype = wr->defaultchoice;
147 /* start_playing = 1;*/
148 /* Update this choice on the main menu */
149 updateMenu((int)GameMenu, wr->defaultchoice);
150 return 1;
151 }
152
MainMenu_action(WalkRadio * wr)153 static int MainMenu_action(WalkRadio *wr)
154 {
155 int i;
156 /* If they selected a submenu, activate it */
157 switch (wr->defaultchoice) {
158 case ColorStyleMenu:
159 case SoundStyleMenu:
160 case PieceStyleMenu:
161 case GameMenu:
162 case OptionsMenu:
163 /* Make sure everything is inactive (not including the main menu) */
164 for (i=0; i<MAX_MENU_CHOICE-1; i++)
165 if (wrg->wr[i].inactive == FALSE) {
166 wrg->wr[i].inactive = TRUE;
167 clear_radio(&wrg->wr[i]);
168 }
169 /* now activate the proper submenu */
170 wrg->wr[wr->defaultchoice].inactive = FALSE;
171 wrg->cur = wr->defaultchoice;
172 break;
173 case 5: /* they chose go! */
174 start_playing = 1;
175 gametype = _local_gametype;
176 break;
177 case 6: /* they chose quit */
178 start_playing = 1;
179 gametype = QUIT;
180 break;
181 default:
182 Debug("Invalid menu choice %d.\n", wr->defaultchoice);
183 }
184 return 1;
185 }
186
187 /***************************************************************************
188 * menu_handler()
189 * Play the MENU-style game.
190 ***************************************************************************/
191 static int
menu_handler(const SDL_Event * event)192 menu_handler(const SDL_Event *event)
193 {
194 int retval;
195 if (event->type == SDL_KEYDOWN) {
196 switch (event->key.keysym.sym) {
197 case SDLK_q:
198 start_playing = 1;
199 _local_gametype = gametype = QUIT;
200 return 1;
201
202 case SDLK_RETURN:
203 if ((event->key.keysym.mod & KMOD_LCTRL) ||
204 (event->key.keysym.mod & KMOD_RCTRL)) {
205 SDL_WM_ToggleFullScreen(screen);
206 return 1;
207 }
208
209 default:
210 break;
211 }
212 }
213 retval = handle_radio_event(wrg, event);
214
215 if (retval != -1)
216 return 1;
217 return 0;
218 }
219
220 /***************************************************************************
221 * play_MENU()
222 * Play the MENU-style game.
223 ***************************************************************************/
224 static int
play_MENU(color_styles cs,piece_styles ps,sound_styles ss,Grid g[],int my_level,AI_Player * aip)225 play_MENU(color_styles cs, piece_styles ps, sound_styles ss,
226 Grid g[], int my_level, AI_Player *aip)
227 {
228 int curtimeleft;
229 int match;
230 int level[2]; /* level array: me + dummy slot */
231 int adjustment[2] = {-1, -1}; /* result of playing a match */
232 int my_adj[3]; /* my three winnings so far */
233 int result;
234 color_style *event_cs[2]; /* pass these to event_loop */
235 sound_style *event_ss[2];
236 AI_Player *event_ai[2];
237 extern int Score[];
238
239 level[0] = my_level; /* starting level */
240 Score[0] = Score[1] = 0;
241 SeedRandom(0);
242
243 while (1) {
244 /* until they give up by pressing 'q'! */
245 /* 5 total minutes for three matches */
246 curtimeleft = 300;
247 my_adj[0] = 0; my_adj[1] = 1; my_adj[2] = 2;
248
249 for (match=0; match<3 && curtimeleft > 0; match++) {
250 /* generate the board */
251 /* draw the background */
252 g[0] = generate_board(10,20,level[0]);
253 draw_background(screen, cs.style[0]->w, g, level, my_adj, NULL,
254 &(aip->name));
255 {
256 int i;
257 for (i=0; i<MAX_MENU_CHOICE; i++)
258 draw_radio(&wrg->wr[i], i == wrg->cur);
259 }
260 /* get the result, but don't update level yet */
261
262 event_cs[0] = event_cs[1] = cs.style[cs.choice];
263 event_ss[0] = event_ss[1] = ss.style[ss.choice];
264 event_ai[0] = aip;
265 event_ai[1] = NULL;
266
267 result = event_loop(screen, ps.style[ps.choice],
268 event_cs, event_ss, g,
269 level, 0, &curtimeleft, 1, adjustment,
270 menu_handler, time(NULL), AI_PLAYER, NO_PLAYER, event_ai);
271 if (result < 0) { /* explicit quit */
272 return -1;
273 }
274 /* reset board */
275 my_adj[match] = adjustment[0];
276 }
277 }
278 }
279
280
281 /***************************************************************************
282 * choose_gametype()
283 * User selects the desired game type, which is returned. Uses walking
284 * radio menus.
285 *********************************************************************PROTO*/
choose_gametype(piece_styles * ps,color_styles * cs,sound_styles * ss,AI_Players * ai)286 int choose_gametype(piece_styles *ps, color_styles *cs,
287 sound_styles *ss, AI_Players *ai)
288 { /* walk-radio menu */
289 int i;
290
291 _local_gametype = gametype;
292
293 if (!wrg) {
294 Calloc(wrg , WalkRadioGroup * , sizeof(*wrg));
295 wrg->n = MAX_MENU_CHOICE;
296 Calloc(wrg->wr, WalkRadio *, wrg->n * sizeof(*wrg->wr));
297 wrg->cur = MainMenu;
298
299 wrg->wr[PieceStyleMenu].n = ps->num_style;
300 Malloc(wrg->wr[PieceStyleMenu].label, char**, sizeof(char*)*ps->num_style);
301 for (i=0; i<ps->num_style; i++)
302 wrg->wr[PieceStyleMenu].label[i] = ps->style[i]->name;
303 wrg->wr[PieceStyleMenu].defaultchoice = ps->choice;
304 wrg->wr[PieceStyleMenu].action = PieceStyleMenu_action;
305
306 wrg->wr[ColorStyleMenu].n = cs->num_style;
307 Malloc(wrg->wr[ColorStyleMenu].label, char**, sizeof(char*)*cs->num_style);
308 for (i=0; i<cs->num_style; i++)
309 wrg->wr[ColorStyleMenu].label[i] = cs->style[i]->name;
310 wrg->wr[ColorStyleMenu].defaultchoice = cs->choice;
311 wrg->wr[ColorStyleMenu].action = ColorStyleMenu_action;
312
313 wrg->wr[SoundStyleMenu].n = ss->num_style;
314 Malloc(wrg->wr[SoundStyleMenu].label, char**, sizeof(char*)*ss->num_style);
315 for (i=0; i<ss->num_style; i++)
316 wrg->wr[SoundStyleMenu].label[i] = ss->style[i]->name;
317 wrg->wr[SoundStyleMenu].defaultchoice = ss->choice;
318 wrg->wr[SoundStyleMenu].action = SoundStyleMenu_action;
319
320 _ss = ss;
321 _cs = cs;
322 _ps = ps;
323
324 wrg->wr[GameMenu].n = 6;
325 Malloc(wrg->wr[GameMenu].label, char**, sizeof(char*)*wrg->wr[GameMenu].n);
326 wrg->wr[GameMenu].label[0] = "Solo Normal Game";
327 wrg->wr[GameMenu].label[1] = "Solo Scoring Marathon";
328 wrg->wr[GameMenu].label[2] = "Solo vs. Computer";
329 wrg->wr[GameMenu].label[3] = "2 Players @ 1 Keyboard";
330 wrg->wr[GameMenu].label[4] = "2 Players, Use Network";
331 wrg->wr[GameMenu].label[5] = "Computer vs. Computer";
332 wrg->wr[GameMenu].defaultchoice = 0;
333 wrg->wr[GameMenu].action = GameMenu_action;
334
335 wrg->wr[OptionsMenu].n = 7;
336 Malloc(wrg->wr[OptionsMenu].label, char**, sizeof(char*)*wrg->wr[OptionsMenu].n);
337 OptionsMenu_setup();
338 wrg->wr[OptionsMenu].defaultchoice = 0;
339 wrg->wr[OptionsMenu].action = OptionsMenu_action;
340
341 /* Make the main menu */
342 wrg->wr[MainMenu].n = 7;
343 Malloc(wrg->wr[MainMenu].label, char**, sizeof(char*)*wrg->wr[MainMenu].n);
344 /* Main menu displays the current value for each choice */
345 wrg->wr[MainMenu].label[0] = wrg->wr[ColorStyleMenu].label[wrg->wr[ColorStyleMenu].defaultchoice];
346 wrg->wr[MainMenu].label[1] = wrg->wr[SoundStyleMenu].label[wrg->wr[SoundStyleMenu].defaultchoice];
347 wrg->wr[MainMenu].label[2] = wrg->wr[PieceStyleMenu].label[wrg->wr[PieceStyleMenu].defaultchoice];
348 wrg->wr[MainMenu].label[3] = wrg->wr[GameMenu].label[(int)_local_gametype];
349 wrg->wr[MainMenu].label[4] = "Special Options";
350 wrg->wr[MainMenu].label[5] = "Play!";
351 wrg->wr[MainMenu].label[6] = "Quit";
352 wrg->wr[MainMenu].defaultchoice = 5;
353 wrg->wr[MainMenu].action = MainMenu_action;
354
355 for (i=0; i<MAX_MENU_CHOICE; i++) {
356 setup_radio(&wrg->wr[i]);
357 }
358
359 wrg->wr[ColorStyleMenu].x = 2*(screen->w/3) +
360 (screen->w/3 - wrg->wr[ColorStyleMenu].area.w)/2;
361 wrg->wr[ColorStyleMenu].y =
362 (screen->h - wrg->wr[ColorStyleMenu].area.h)/2;
363 wrg->wr[ColorStyleMenu].inactive = TRUE;
364
365 wrg->wr[SoundStyleMenu].x = 2*(screen->w/3) +
366 (screen->w/3 - wrg->wr[SoundStyleMenu].area.w)/2;
367 wrg->wr[SoundStyleMenu].y =
368 (screen->h - wrg->wr[SoundStyleMenu].area.h)/2;
369 wrg->wr[SoundStyleMenu].inactive = TRUE;
370
371 wrg->wr[OptionsMenu].x = 2*(screen->w/3) +
372 (screen->w/3 - wrg->wr[OptionsMenu].area.w)/2;
373 wrg->wr[OptionsMenu].y =
374 (screen->h - wrg->wr[OptionsMenu].area.h)/2;
375 wrg->wr[OptionsMenu].inactive = TRUE;
376
377 wrg->wr[PieceStyleMenu].x = 2*(screen->w/3) +
378 (screen->w/3 - wrg->wr[PieceStyleMenu].area.w)/2;
379 wrg->wr[PieceStyleMenu].y =
380 (screen->h - wrg->wr[PieceStyleMenu].area.h)/2;
381 wrg->wr[PieceStyleMenu].inactive = TRUE;
382
383 wrg->wr[GameMenu].x = 2*(screen->w/3) +
384 (screen->w/3 - wrg->wr[GameMenu].area.w)/2;
385 wrg->wr[GameMenu].y =
386 (screen->h - wrg->wr[GameMenu].area.h)/2;
387 wrg->wr[GameMenu].inactive = TRUE;
388
389 wrg->wr[MainMenu].x = (screen->w/3 - wrg->wr[MainMenu].area.w)/2;
390 wrg->wr[MainMenu].y = (screen->h - wrg->wr[MainMenu].area.h)/2;
391 wrg->wr[MainMenu].inactive = FALSE;
392 }
393
394 clear_screen_to_flame();
395 SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY*10,
396 SDL_DEFAULT_REPEAT_INTERVAL*10);
397
398 start_playing = 0;
399 while (1) {
400 Grid g[2];
401 gametype = DEMO;
402 play_MENU(*cs, *ps, *ss, g, Options.faster_levels ? 5 : 10,
403 &ai->player[ZEROTO(ai->n)]);
404 if (start_playing) {
405 return 0;
406 }
407 }
408 }
409
410 /*
411 * $Log: gamemenu.c,v $
412 * Revision 1.30 2001/01/06 00:25:23 weimer
413 * changes so that we remember things ...
414 *
415 * Revision 1.29 2001/01/05 21:38:01 weimer
416 * more key changes
417 *
418 * Revision 1.28 2001/01/05 21:12:32 weimer
419 * advance to atris 1.0.5, add support for ".atrisrc" and changing the
420 * keyboard repeat rate
421 *
422 * Revision 1.27 2000/11/10 18:16:48 weimer
423 * changes for Atris 1.0.4 - three new special options
424 *
425 * Revision 1.26 2000/11/06 04:16:10 weimer
426 * "power pieces" plus images
427 *
428 * Revision 1.25 2000/11/06 04:06:44 weimer
429 * option menu
430 *
431 * Revision 1.24 2000/11/06 01:24:43 wkiri
432 * Removed 'quit' from the game type menu.
433 *
434 * Revision 1.23 2000/11/06 01:22:40 wkiri
435 * Updated menu system.
436 *
437 * Revision 1.22 2000/11/02 03:06:20 weimer
438 * better interface for walk-radio menus: we are now ready for Kiri to change
439 * them to add run-time options ...
440 *
441 * Revision 1.21 2000/11/01 03:53:06 weimer
442 * modifications for version 1.0.1: you can pick your starting level, you can
443 * pick the AI difficulty factor, the game is better about placing new pieces
444 * when there is garbage, when things fall out of your control they now fall
445 * at a uniform rate ...
446 *
447 * Revision 1.20 2000/10/29 22:55:01 weimer
448 * networking consistency checks (you must have the same number of doodads):
449 * special hotkey 'f' in main menu toggles full screen mode
450 * added depth specification on the command line
451 * automatically search for the darkest non-black color ...
452 *
453 * Revision 1.19 2000/10/29 21:23:28 weimer
454 * One last round of header-file changes to reflect my newest and greatest
455 * knowledge of autoconf/automake. Now if you fail to have some bizarro
456 * function, we try to go on anyway unless it is vastly needed.
457 *
458 * Revision 1.18 2000/10/29 17:23:13 weimer
459 * incorporate "xflame" flaming background for added spiffiness ...
460 *
461 * Revision 1.17 2000/10/29 00:17:39 weimer
462 * added support for a system independent random number generator
463 *
464 * Revision 1.16 2000/10/28 21:52:56 weimer
465 * you can now press 'Q' to quit from the main menu!
466 *
467 * Revision 1.15 2000/10/28 16:40:17 weimer
468 * Further changes: we can now build .tar.gz files and RPMs!
469 *
470 * Revision 1.14 2000/10/22 22:05:51 weimer
471 * Added a few new sounds ...
472 *
473 * Revision 1.13 2000/10/21 01:14:42 weimer
474 * massic autoconf/automake restructure ...
475 *
476 * Revision 1.12 2000/10/18 23:57:49 weimer
477 * general fixup, color changes, display changes.
478 * Notable: "Safe" Blits and Updates now perform "clipping". No more X errors,
479 * we hope!
480 *
481 * Revision 1.11 2000/10/18 02:04:02 weimer
482 * playability changes ...
483 *
484 * Revision 1.10 2000/10/13 15:41:53 weimer
485 * revamped AI support, now you can pick your AI and have AI duels (such fun!)
486 * the mighty Aliz AI still crashes a bit, though ... :-)
487 *
488 * Revision 1.9 2000/10/12 22:21:25 weimer
489 * display changes, more multi-local-play threading (e.g., myScore ->
490 * Score[0]), that sort of thing ...
491 *
492 * Revision 1.8 2000/10/12 19:17:08 weimer
493 * Further support for AI players and multiple game types.
494 *
495 * Revision 1.7 2000/10/12 01:38:07 weimer
496 * added initial support for persistent player identities
497 *
498 * Revision 1.6 2000/10/12 00:49:08 weimer
499 * added "AI" support and walking radio menus for initial game configuration
500 *
501 * Revision 1.5 2000/09/09 17:05:35 wkiri
502 * Hideous log changes (Wes: how dare you include a comment character!)
503 *
504 * Revision 1.4 2000/09/09 16:58:27 weimer
505 * Sweeping Change of Ultimate Mastery. Main loop restructuring to clean up
506 * main(), isolate the behavior of the three game types. Move graphic files
507 * into graphics/-, style files into styles/-, remove some unused files,
508 * add game flow support (breaks between games, remembering your level within
509 * this game), multiplayer support for the event loop, some global variable
510 * cleanup. All that and a bag of chips!
511 *
512 * Revision 1.3 2000/09/04 22:49:51 wkiri
513 * gamemenu now uses the new nifty menus. Also, added delete_menu.
514 *
515 * Revision 1.2 2000/09/04 19:48:02 weimer
516 * interim menu for choosing among game styles, button changes (two states)
517 *
518 * Revision 1.1 2000/09/03 19:41:30 wkiri
519 * Now allows you to choose the game type (though it doesn't do anything yet).
520 *
521 */
522