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