1 
2 /* This file handles the controls configuration and updating the keystrokes
3 */
4 
5 #include <string.h>
6 #include <ctype.h>
7 
8 #include "Maelstrom_Globals.h"
9 #include "load.h"
10 #include "dialog.h"
11 
12 #define MAELSTROM_DATA	".Maelstrom-data"
13 
14 /* Savable and configurable controls/data */
15 
16 /* Pause        Shield     Thrust  TurnR      TurnL     Fire     Quit  */
17 #ifdef FAITHFUL_SPECS
18 Controls controls =
19 { SDLK_CAPSLOCK,SDLK_SPACE,SDLK_UP,SDLK_RIGHT,SDLK_LEFT,SDLK_TAB,SDLK_ESCAPE };
20 #else
21 Controls controls =
22    { SDLK_PAUSE,SDLK_SPACE,SDLK_UP,SDLK_RIGHT,SDLK_LEFT,SDLK_TAB,SDLK_ESCAPE };
23 #endif
24 
25 #ifdef MOVIE_SUPPORT
26 int	gMovie = 0;
27 #endif
28 Uint8 gSoundLevel = 4;
29 Uint8 gGammaCorrect = 3;
30 
31 
32 /* Map a keycode to a key name */
KeyName(SDLKey keycode,char * namebuf)33 void KeyName(SDLKey keycode, char *namebuf)
34 {
35 	char *name, ch;
36 	int starting;
37 
38 	/* Get the name of the key */
39 	name = SDL_GetKeyName(keycode);
40 
41 	/* Add "arrow" to the arrow keys */
42 	if ( strcmp(name, "up") == 0 ) {
43 		name = "up arrow";
44 	} else
45 	if ( strcmp(name, "down") == 0 ) {
46 		name = "down arrow";
47 	} else
48 	if ( strcmp(name, "right") == 0 ) {
49 		name = "right arrow";
50 	} else
51 	if ( strcmp(name, "left") == 0 ) {
52 		name = "left arrow";
53 	}
54 	/* Make the key names uppercased */
55 	for ( starting = 1; *name; ++name ) {
56 		ch = *name;
57 		if ( starting ) {
58 			if ( islower(ch) )
59 				ch = toupper(ch);
60 			starting = 0;
61 		} else {
62 			if ( ch == ' ' )
63 				starting = 1;
64 		}
65 		*namebuf++ = ch;
66 	}
67 	*namebuf = '\0';
68 }
69 
OpenData(char * mode,char ** fname)70 static FILE *OpenData(char *mode, char **fname)
71 {
72 	static char datafile[BUFSIZ];
73 	char *home;
74 	FILE *data;
75 
76 	if ( (home=getenv("HOME")) == NULL ) {
77 		if ( strcmp(CUR_DIR, DIR_SEP) != 0 ) {
78 			home = CUR_DIR;
79 		} else {
80 			home="";
81 		}
82 	}
83 	if ( fname ) {
84 		*fname = datafile;
85 	}
86 	snprintf(datafile, sizeof(datafile), "%s"DIR_SEP"%s", home, MAELSTROM_DATA);
87 	if ( (data=fopen(datafile, mode)) == NULL )
88 		return(NULL);
89 	return(data);
90 }
91 
LoadControls(void)92 void LoadControls(void)
93 {
94 	char  buffer[BUFSIZ], *datafile;
95 	FILE *data;
96 
97 	/* Open our control data file */
98 	data = OpenData("r", &datafile);
99 	if ( data == NULL ) {
100 		return;
101 	}
102 
103 	/* Read the controls */
104 	if ( (fread(buffer, 1, 5, data) != 5) || strncmp(buffer, "MAEL3", 5) ) {
105 		error(
106 		"Warning: Data file '%s' is corrupt! (will fix)\n", datafile);
107 		fclose(data);
108 		return;
109 	}
110 	fread(&gSoundLevel, sizeof(gSoundLevel), 1, data);
111 	fread(&controls, sizeof(controls), 1, data);
112 	fread(&gGammaCorrect, sizeof(gGammaCorrect), 1, data);
113 	fclose(data);
114 }
115 
SaveControls(void)116 void SaveControls(void)
117 {
118 	char  *datafile, *newmode;
119 	FILE *data;
120 
121 	/* Don't clobber existing joystick data */
122 	if ( (data=OpenData("r", NULL)) != NULL ) {
123 		newmode = "r+";
124 		fclose(data);
125 	} else
126 		newmode = "w";
127 
128 	if ( (data=OpenData(newmode, &datafile)) == NULL ) {
129 		error("Warning: Couldn't save controls to %s\n", datafile);
130 		return;
131 	}
132 
133 	fwrite("MAEL3", 1, 5, data);
134 	fwrite(&gSoundLevel, sizeof(gSoundLevel), 1, data);
135 	fwrite(&controls, sizeof(controls), 1, data);
136 	fwrite(&gGammaCorrect, sizeof(gGammaCorrect), 1, data);
137 	fclose(data);
138 }
139 
140 #define FIRE_CTL	0
141 #define THRUST_CTL	1
142 #define SHIELD_CTL	2
143 #define TURNR_CTL	3
144 #define TURNL_CTL	4
145 #define PAUSE_CTL	5
146 #define QUIT_CTL	6
147 #define NUM_CTLS	7
148 
149 #define SP		3
150 
151 #define CTL_DIALOG_WIDTH	482
152 #define CTL_DIALOG_HEIGHT	300
153 
154 Controls newcontrols;
155 static struct {
156 	char *label;
157 	int  yoffset;
158 	SDLKey *control;
159 } checkboxes[] = {
160 	{ "Fire",	0*BOX_HEIGHT+0*SP, &newcontrols.gFireControl },
161 	{ "Thrust",	1*BOX_HEIGHT+1*SP, &newcontrols.gThrustControl },
162 	{ "Shield",	2*BOX_HEIGHT+2*SP, &newcontrols.gShieldControl },
163 	{ "Turn Clockwise", 3*BOX_HEIGHT+3*SP, &newcontrols.gTurnRControl },
164 	{ "Turn Counter-Clockwise",
165 			4*BOX_HEIGHT+4*SP, &newcontrols.gTurnLControl },
166 	{ "Pause",	5*BOX_HEIGHT+5*SP, &newcontrols.gPauseControl },
167 	{ "Abort Game",	6*BOX_HEIGHT+6*SP, &newcontrols.gQuitControl },
168 };
169 
170 static int X=0;
171 static int Y=0;
172 static MFont *chicago;
173 static SDL_Surface *keynames[NUM_CTLS];
174 static int currentbox, valid;
175 
OK_callback(void)176 static int OK_callback(void) {
177 	valid = 1;
178 	return(1);
179 }
Cancel_callback(void)180 static int Cancel_callback(void) {
181 	valid = 0;
182 	return(1);
183 }
BoxKeyPress(SDL_keysym key,int * doneflag)184 static void BoxKeyPress(SDL_keysym key, int *doneflag)
185 {
186 	SDL_Color black = { 0x00, 0x00, 0x00, 0 };
187 	SDL_Color white = { 0xFF, 0xFF, 0xFF, 0 };
188 	int i;
189 	char keyname[128];
190 
191 	if ( key.sym == *checkboxes[currentbox].control )
192 		return;
193 
194 	/* Make sure the key isn't in use! */
195 	for ( i=0; i<NUM_CTLS; ++i ) {
196 		if ( key.sym == *checkboxes[i].control ) {
197 			key.sym = (SDLKey)*checkboxes[currentbox].control;
198 
199 			/* Clear the current text */
200 			fontserv->InvertText(keynames[currentbox]);
201 			screen->QueueBlit(
202 				X+96+(BOX_WIDTH-keynames[currentbox]->w)/2,
203 				Y+75+SP+checkboxes[currentbox].yoffset,
204 						keynames[currentbox], NOCLIP);
205 			screen->Update();
206 			fontserv->FreeText(keynames[currentbox]);
207 
208 			/* Blit the new message */
209 			strcpy(keyname, "That key is in use!");
210 			keynames[currentbox] = fontserv->TextImage(keyname,
211 					chicago, STYLE_NORM, black, white);
212 			screen->QueueBlit(
213 				X+96+(BOX_WIDTH-keynames[currentbox]->w)/2,
214 				Y+75+SP+checkboxes[currentbox].yoffset,
215 					keynames[currentbox], NOCLIP);
216 			screen->Update();
217 			SDL_Delay(1000);
218 			break;
219 		}
220 	}
221 
222 	/* Clear the current text */
223 	fontserv->InvertText(keynames[currentbox]);
224 	screen->QueueBlit(X+96+(BOX_WIDTH-keynames[currentbox]->w)/2,
225 				Y+75+SP+checkboxes[currentbox].yoffset,
226 						keynames[currentbox], NOCLIP);
227 	screen->Update();
228 	fontserv->FreeText(keynames[currentbox]);
229 
230 	/* Display the new key */
231 	*checkboxes[currentbox].control = key.sym;
232 	KeyName(*checkboxes[currentbox].control, keyname);
233 	keynames[currentbox] = fontserv->TextImage(keyname, chicago, STYLE_NORM,
234 								black, white);
235 	screen->QueueBlit(X+96+(BOX_WIDTH-keynames[currentbox]->w)/2,
236 				Y+75+SP+checkboxes[currentbox].yoffset,
237 						keynames[currentbox], NOCLIP);
238 	screen->Update();
239 }
240 
ConfigureControls(void)241 void ConfigureControls(void)
242 {
243 #ifdef FAITHFUL_SPECS
244 	static char *C_text1 =
245 		"While playing Maelstrom, CAPS LOCK pauses the game and";
246 	static char *C_text2 =
247 		"ESC aborts the game.";
248 	SDL_Surface *text1, *text2;
249 #endif
250 	Uint32 black;
251 	int i;
252 	char keyname[128];
253 	Maclike_Dialog *dialog;
254 	SDL_Surface *splash;
255 	Mac_Button *cancel, *okay;
256 	Mac_RadioList *radiobuttons;
257 	Mac_Dialog *boxes;
258 
259 
260 	/* Set up all the components of the dialog box */
261 	black = screen->MapRGB(0x00, 0x00, 0x00);
262 	if ( (chicago = fontserv->NewFont("Chicago", 12)) == NULL ) {
263 		error("Can't use Chicago font!\n");
264 		return;
265 	}
266 	if ( (splash = Load_Title(screen, 100)) == NULL ) {
267 		error("Can't load configuration splash!\n");
268 		delete chicago;
269 		return;
270 	}
271 	X=(SCREEN_WIDTH-CTL_DIALOG_WIDTH)/2;
272 	Y=(SCREEN_HEIGHT-CTL_DIALOG_HEIGHT)/2;
273 	dialog = new Maclike_Dialog(X, Y, CTL_DIALOG_WIDTH, CTL_DIALOG_HEIGHT,
274 									screen);
275 	dialog->Add_Image(splash, 4, 4);
276 #ifdef FAITHFUL_SPECS
277 	text1 = fontserv->TextImage(C_text1,chicago,STYLE_NORM,0x00,0x00,0x00);
278 	text2 = fontserv->TextImage(C_text2,chicago,STYLE_NORM,0x00,0x00,0x00);
279 	dialog->Add_Image(text1, 66, 216);
280 	dialog->Add_Image(text2, 66, 232);
281 #endif
282 	valid = 0;
283 	cancel = new Mac_Button(291, 265, BUTTON_WIDTH, BUTTON_HEIGHT,
284 				"Cancel", chicago, fontserv, Cancel_callback);
285 	dialog->Add_Dialog(cancel);
286 	okay = new Mac_Button(291+BUTTON_WIDTH+26, 265,
287 					BUTTON_WIDTH, BUTTON_HEIGHT,
288 				"OK", chicago, fontserv, OK_callback);
289 	dialog->Add_Dialog(okay);
290 	memcpy(&newcontrols, &controls, sizeof(controls));
291 	radiobuttons = new Mac_RadioList(&currentbox, X+266, Y+75,
292 							chicago, fontserv);
293 	currentbox = FIRE_CTL;
294 	for ( i=0; i<NUM_CTLS; ++i ) {
295 		KeyName(*checkboxes[i].control, keyname);
296 		keynames[i] = fontserv->TextImage(keyname, chicago, STYLE_NORM,
297 							0x00, 0x00, 0x00);
298 		/* Only display "Fire" through "Turn Counter-Clockwise" */
299 #ifdef FAITHFUL_SPECS
300 		if ( i <  PAUSE_CTL ) {
301 #else
302 		if ( i <  NUM_CTLS ) {
303 #endif
304 			radiobuttons->Add_Radio(263, 71+checkboxes[i].yoffset,
305 							checkboxes[i].label);
306 			dialog->Add_Rectangle(92, 71+checkboxes[i].yoffset,
307 						BOX_WIDTH, BOX_HEIGHT, black);
308 			dialog->Add_Image(keynames[i],
309 					92+(BOX_WIDTH-keynames[i]->w)/2,
310 						71+SP+checkboxes[i].yoffset);
311 		}
312 	}
313 	dialog->Add_Dialog(radiobuttons);
314 	boxes = new Mac_Dialog(92, 71);
315 	boxes->SetKeyPress(BoxKeyPress);
316 	dialog->Add_Dialog(boxes);
317 
318 	/* Run the dialog box */
319 	dialog->Run(EXPAND_STEPS);
320 
321 	/* Clean up and return */
322 	screen->FreeImage(splash);
323 #ifdef FAITHFUL_SPECS
324 	fontserv->FreeText(text1);
325 	fontserv->FreeText(text2);
326 #endif
327 	for ( i=0; i<NUM_CTLS; ++i ) {
328 		fontserv->FreeText(keynames[i]);
329 	}
330 	delete chicago;
331 	delete dialog;
332 	if ( valid ) {
333 		memcpy(&controls, &newcontrols, sizeof(controls));
334 		SaveControls();
335 	}
336 	return;
337 }
338 
339 static void HandleEvent(SDL_Event *event)
340 {
341 	SDLKey key;
342 
343 	switch (event->type) {
344 #ifdef SDL_INIT_JOYSTICK
345 		/* -- Handle joystick axis motion */
346 		case SDL_JOYAXISMOTION:
347 			/* X-Axis - rotate right/left */
348 			if ( event->jaxis.axis == 0 ) {
349 				if ( event->jaxis.value < -8000 ) {
350 					SetControl(LEFT_KEY, 1);
351 					SetControl(RIGHT_KEY, 0);
352 				} else
353 				if ( event->jaxis.value > 8000 ) {
354 					SetControl(RIGHT_KEY, 1);
355 					SetControl(LEFT_KEY, 0);
356 				} else {
357 					SetControl(LEFT_KEY, 0);
358 					SetControl(RIGHT_KEY, 0);
359 				}
360 			} else
361 			/* Y-Axis - accelerate */
362 			if ( event->jaxis.axis == 1 ) {
363 				if ( event->jaxis.value < -8000 ) {
364 					SetControl(THRUST_KEY, 1);
365 				} else {
366 					SetControl(THRUST_KEY, 0);
367 				}
368 			}
369 			break;
370 
371 		/* -- Handle joystick button presses/releases */
372 		case SDL_JOYBUTTONDOWN:
373 		case SDL_JOYBUTTONUP:
374 			if ( event->jbutton.state == SDL_PRESSED ) {
375 				if ( event->jbutton.button == 0 ) {
376 					SetControl(FIRE_KEY, 1);
377 				} else
378 				if ( event->jbutton.button == 1 ) {
379 					SetControl(SHIELD_KEY, 1);
380 				}
381 			} else {
382 				if ( event->jbutton.button == 0 ) {
383 					SetControl(FIRE_KEY, 0);
384 				} else
385 				if ( event->jbutton.button == 1 ) {
386 					SetControl(SHIELD_KEY, 0);
387 				}
388 			}
389 			break;
390 #endif
391 
392 		/* -- Handle key presses/releases */
393 		case SDL_KEYDOWN:
394 			/* -- Handle ALT-ENTER hotkey */
395 	                if ( (event->key.keysym.sym == SDLK_RETURN) &&
396 			     (event->key.keysym.mod & KMOD_ALT) ) {
397 				screen->ToggleFullScreen();
398 				break;
399 			}
400 		case SDL_KEYUP:
401 			/* -- Handle normal key bindings */
402 			key = event->key.keysym.sym;
403 			if ( event->key.state == SDL_PRESSED ) {
404 				/* Check for various control keys */
405 				if ( key == controls.gFireControl )
406 					SetControl(FIRE_KEY, 1);
407 				else if ( key == controls.gTurnRControl )
408 					SetControl(RIGHT_KEY, 1);
409 				else if ( key == controls.gTurnLControl )
410 					SetControl(LEFT_KEY, 1);
411 				else if ( key == controls.gShieldControl )
412 					SetControl(SHIELD_KEY, 1);
413 				else if ( key == controls.gThrustControl )
414 					SetControl(THRUST_KEY, 1);
415 				else if ( key == controls.gPauseControl )
416 					SetControl(PAUSE_KEY, 1);
417 				else if ( key == controls.gQuitControl )
418 					SetControl(ABORT_KEY, 1);
419 				else if ( SpecialKey(event->key.keysym) == 0 )
420 					/* The key has been handled */;
421 				else if ( key == SDLK_F3 ) {
422 					/* Special key --
423 						Do a screen dump here.
424 					 */
425 					screen->ScreenDump("ScreenShot",
426 								0, 0, 0, 0);
427 #ifdef MOVIE_SUPPORT
428 				} else if ( key == XK_F5 ) {
429 					/* Special key --
430 						Toggle movie function.
431 					 */
432 					extern int SelectMovieRect(void);
433 					if ( ! gMovie )
434 						gMovie = SelectMovieRect();
435 					else
436 						gMovie = 0;
437 mesg("Movie is %s...\n", gMovie ? "started" : "stopped");
438 #endif
439 				}
440 			} else {
441 				/* Update control key status */
442 				if ( key == controls.gFireControl )
443 					SetControl(FIRE_KEY, 0);
444 				else if ( key == controls.gTurnRControl )
445 					SetControl(RIGHT_KEY, 0);
446 				else if ( key == controls.gTurnLControl )
447 					SetControl(LEFT_KEY, 0);
448 				else if ( key == controls.gShieldControl )
449 					SetControl(SHIELD_KEY, 0);
450 				else if ( key == controls.gThrustControl )
451 					SetControl(THRUST_KEY, 0);
452 			}
453 			break;
454 
455 		case SDL_QUIT:
456 			SetControl(ABORT_KEY, 1);
457 			break;
458 	}
459 }
460 
461 
462 /* This function gives a good way to delay a specified amount of time
463    while handling keyboard/joystick events, or just to poll for events.
464 */
465 void HandleEvents(int timeout)
466 {
467 	SDL_Event event;
468 
469 	do {
470 		while ( SDL_PollEvent(&event) ) {
471 			HandleEvent(&event);
472 		}
473 		if ( timeout ) {
474 			/* Delay 1/60 of a second... */
475 			Delay(1);
476 		}
477 	} while ( timeout-- );
478 }
479 
480 int DropEvents(void)
481 {
482 	SDL_Event event;
483 	int keys = 0;
484 
485 	while ( SDL_PollEvent(&event) ) {
486 		if ( event.type == SDL_KEYDOWN ) {
487 			++keys;
488 		}
489 	}
490 	return(keys);
491 }
492 
493 #define DAWN_DIALOG_WIDTH	318
494 #define DAWN_DIALOG_HEIGHT	194
495 
496 void ShowDawn(void)
497 {
498 	static char *D_text[6] = {
499 		"No eternal reward will forgive us",
500 		"now",
501 		    "for",
502 		        "wasting",
503 		                "the",
504 		                    "dawn."
505 	};
506 	MFont *chicago;
507 	SDL_Surface *splash, *text[6];
508 	Maclike_Dialog *dialog;
509 	Mac_Button *OK;
510 	int i, x, y, X, Y;
511 
512 	/* Set up all the components of the dialog box */
513 #ifdef CENTER_DIALOG
514 	X=(SCREEN_WIDTH-DAWN_DIALOG_WIDTH)/2;
515 	Y=(SCREEN_HEIGHT-DAWN_DIALOG_HEIGHT)/2;
516 #else	/* The way it is on the original Maelstrom */
517 	X=160;
518 	Y=73;
519 #endif
520 	if ( (chicago = fontserv->NewFont("Chicago", 12)) == NULL ) {
521 		error("Can't use Chicago font!\n");
522 		return;
523 	}
524 	if ( (splash = GetCIcon(screen, 103)) == NULL ) {
525 		error("Can't load alien dawn splash!\n");
526 		return;
527 	}
528 	dialog = new Maclike_Dialog(X, Y, DAWN_DIALOG_WIDTH, DAWN_DIALOG_HEIGHT,
529 									screen);
530 	x = y = 19;
531 	dialog->Add_Image(splash, x, y);
532 	x += (splash->w+26);
533 	text[0] = fontserv->TextImage(D_text[0], chicago, STYLE_NORM,
534 							0x00, 0x00, 0x00);
535 	dialog->Add_Image(text[0], x, y);
536 	for ( i=1; i<6; ++i ) {
537 		y += (text[i-1]->h+2);
538 		text[i] = fontserv->TextImage(D_text[i], chicago, STYLE_NORM,
539 							0x00, 0x00, 0x00);
540 		dialog->Add_Image(text[i], x, y);
541 		x += (text[i]->w+2);
542 	}
543 	OK = new Mac_DefaultButton(210, 160, 90, BUTTON_HEIGHT,
544 						"OK", chicago, fontserv, NULL);
545 	dialog->Add_Dialog(OK);
546 
547 	/* Run the dialog box */
548 	dialog->Run(EXPAND_STEPS);
549 
550 	/* Clean up and return */
551 	screen->FreeImage(splash);
552 	for ( i=0; i<6; ++i )
553 		fontserv->FreeText(text[i]);
554 	delete chicago;
555 	delete dialog;
556 	return;
557 }
558 
559