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(¤tbox, 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