1 /* ------------------------------------------------------------- */
2 /* */
3 /* Maelstrom */
4 /* By Andrew Welch */
5 /* */
6 /* Ported to Linux (Spring 1995) */
7 /* Ported to Win95 (Fall 1996) -- not releasable */
8 /* Ported to SDL (Fall 1997) */
9 /* By Sam Lantinga (slouken@devolution.com) */
10 /* */
11 /* ------------------------------------------------------------- */
12
13 #include "Maelstrom_Globals.h"
14 #include "buttonlist.h"
15 #include "load.h"
16 #include "fastrand.h"
17 #include "checksum.h"
18
19 /* External functions used in this file */
20 extern int DoInitializations(Uint32 video_flags); /* init.cc */
21
22 static char *Version =
23 "Maelstrom v1.4.3 (GPL version 3.0.6) -- 10/19/2002 by Sam Lantinga\n";
24
25 // Global variables set in this file...
26 int gStartLives;
27 int gStartLevel;
28 Bool gUpdateBuffer;
29 Bool gRunning;
30 int gNoDelay;
31
32 // Local variables in this file...
33 static ButtonList buttons;
34
35 // Local functions in this file...
36 static void DrawMainScreen(void);
37 static void DrawSoundLevel(void);
38 static void DrawKey(MPoint *pt, char *ch, char *str, void (*callback)(void));
39
40 // Main Menu actions:
RunDoAbout(void)41 static void RunDoAbout(void)
42 {
43 gNoDelay = 0;
44 Delay(SOUND_DELAY);
45 sound->PlaySound(gNovaAppears, 5);
46 DoAbout();
47 }
RunConfigureControls(void)48 static void RunConfigureControls(void)
49 {
50 Delay(SOUND_DELAY);
51 sound->PlaySound(gHomingAppears, 5);
52 ConfigureControls();
53 }
RunPlayGame(void)54 static void RunPlayGame(void)
55 {
56 gStartLives = 3;
57 gStartLevel = 1;
58 gNoDelay = 0;
59 sound->PlaySound(gNewLife, 5);
60 Delay(SOUND_DELAY);
61 NewGame();
62 Message(NULL); /* Clear any messages */
63 }
RunQuitGame(void)64 static void RunQuitGame(void)
65 {
66 Delay(SOUND_DELAY);
67 sound->PlaySound(gMultiplierGone, 5);
68 while ( sound->Playing() )
69 Delay(SOUND_DELAY);
70 gRunning = false;
71 }
IncrementSound(void)72 static void IncrementSound(void)
73 {
74 if ( gSoundLevel < 8 ) {
75 sound->Volume(++gSoundLevel);
76 sound->PlaySound(gNewLife, 5);
77
78 /* -- Draw the new sound level */
79 DrawSoundLevel();
80 }
81 }
DecrementSound(void)82 static void DecrementSound(void)
83 {
84 if ( gSoundLevel > 0 ) {
85 sound->Volume(--gSoundLevel);
86 sound->PlaySound(gNewLife, 5);
87
88 /* -- Draw the new sound level */
89 DrawSoundLevel();
90 }
91 }
SetSoundLevel(int volume)92 static void SetSoundLevel(int volume)
93 {
94 /* Make sure the device is working */
95 sound->Volume(volume);
96
97 /* Set the new sound level! */
98 gSoundLevel = volume;
99 sound->PlaySound(gNewLife, 5);
100
101 /* -- Draw the new sound level */
102 DrawSoundLevel();
103 }
104
RunZapScores(void)105 static void RunZapScores(void)
106 {
107 Delay(SOUND_DELAY);
108 sound->PlaySound(gMultShotSound, 5);
109 if ( ZapHighScores() ) {
110 /* Fade the screen and redisplay scores */
111 screen->Fade();
112 Delay(SOUND_DELAY);
113 sound->PlaySound(gExplosionSound, 5);
114 gUpdateBuffer = true;
115 }
116 }
117
118 /* ----------------------------------------------------------------- */
119 /* -- Run a graphics speed test. */
RunSpeedTest(void)120 static void RunSpeedTest(void)
121 {
122 const int test_reps = 100; /* How many full cycles to run */
123
124 Uint32 then, now;
125 int i, frame, x=((640/2)-16), y=((480/2)-16), onscreen=0;
126
127 screen->Clear();
128 then = SDL_GetTicks();
129 for ( i=0; i<test_reps; ++i ) {
130 for ( frame=0; frame<SHIP_FRAMES; ++frame ) {
131 if ( onscreen ) {
132 screen->Clear(x, y, 32, 32);
133 } else {
134 onscreen = 1;
135 }
136 screen->QueueBlit(x, y, gPlayerShip->sprite[frame]);
137 screen->Update();
138 }
139 }
140 now = SDL_GetTicks();
141 mesg("Graphics speed test took %d microseconds per cycle.\r\n",
142 ((now-then)/test_reps));
143 }
144
145 /* ----------------------------------------------------------------- */
146 /* -- Print a Usage message and quit.
147 In several places we depend on this function exiting.
148 */
149 static char *progname;
PrintUsage(void)150 void PrintUsage(void)
151 {
152 error("\nUsage: %s [-netscores] -printscores\n", progname);
153 error("or\n");
154 error("Usage: %s <options>\n\n", progname);
155 error("Where <options> can be any of:\n\n"
156 " -fullscreen # Run Maelstrom in full-screen mode\n"
157 " -gamma [0-8] # Set the gamma correction\n"
158 " -volume [0-8] # Set the sound volume\n"
159 " -netscores # Use the world-wide network score server\n"
160 );
161 LogicUsage();
162 error("\n");
163 exit(1);
164 }
165
166 /* ----------------------------------------------------------------- */
167 /* -- Blitter main program */
main(int argc,char * argv[])168 int main(int argc, char *argv[])
169 {
170 /* Command line flags */
171 int doprinthigh = 0;
172 int speedtest = 0;
173 Uint32 video_flags = SDL_SWSURFACE;
174
175 /* Normal variables */
176 SDL_Event event;
177 LibPath::SetExePath(argv[0]);
178
179 #ifndef __WIN95__
180 /* The first thing we do is calculate our checksum */
181 (void) checksum();
182 #endif /* ! Win95 */
183
184 /* Seed the random number generator */
185 SeedRandom(0L);
186 /* Initialize the controls */
187 LoadControls();
188
189 /* Initialize game logic data structures */
190 if ( InitLogicData() < 0 ) {
191 exit(1);
192 }
193
194 /* Parse command line arguments */
195 for ( progname=argv[0]; --argc; ++argv ) {
196 if ( strcmp(argv[1], "-fullscreen") == 0 ) {
197 video_flags |= SDL_FULLSCREEN;
198 } else
199 if ( strcmp(argv[1], "-gamma") == 0 ) {
200 int gammacorrect;
201
202 if ( ! argv[2] ) { /* Print the current gamma */
203 mesg("Current Gamma correction level: %d\n",
204 gGammaCorrect);
205 exit(0);
206 }
207 if ( (gammacorrect=atoi(argv[2])) < 0 ||
208 gammacorrect > 8 ) {
209 error(
210 "Gamma correction value must be between 0 and 8. -- Exiting.\n");
211 exit(1);
212 }
213 /* We need to update the gamma */
214 gGammaCorrect = gammacorrect;
215 SaveControls();
216
217 ++argv;
218 --argc;
219 }
220 else if ( strcmp(argv[1], "-volume") == 0 ) {
221 int volume;
222
223 if ( ! argv[2] ) { /* Print the current volume */
224 mesg("Current volume level: %d\n",
225 gSoundLevel);
226 exit(0);
227 }
228 if ( (volume=atoi(argv[2])) < 0 || volume > 8 ) {
229 error(
230 "Volume must be a number between 0 and 8. -- Exiting.\n");
231 exit(1);
232 }
233 /* We need to update the volume */
234 gSoundLevel = volume;
235 SaveControls();
236
237 ++argv;
238 --argc;
239 }
240 #define CHECKSUM_DEBUG
241 #ifdef CHECKSUM_DEBUG
242 else if ( strcmp(argv[1], "-checksum") == 0 ) {
243 mesg("Checksum = %s\n", get_checksum(NULL, 0));
244 exit(0);
245 }
246 #endif /* CHECKSUM_DEBUG */
247 else if ( strcmp(argv[1], "-printscores") == 0 )
248 doprinthigh = 1;
249 else if ( strcmp(argv[1], "-netscores") == 0 )
250 gNetScores = 1;
251 else if ( strcmp(argv[1], "-speedtest") == 0 )
252 speedtest = 1;
253 else if ( LogicParseArgs(&argv, &argc) == 0 ) {
254 /* LogicParseArgs() took care of everything */;
255 } else if ( strcmp(argv[1], "-version") == 0 ) {
256 error("%s", Version);
257 exit(0);
258 } else {
259 PrintUsage();
260 }
261 }
262
263 /* Do we just want the high scores? */
264 if ( doprinthigh ) {
265 PrintHighScores();
266 exit(0);
267 }
268
269 /* Make sure we have a valid player list (netlogic) */
270 if ( InitLogic() < 0 )
271 exit(1);
272
273 /* Initialize everything. :) */
274 if ( DoInitializations(video_flags) < 0 ) {
275 /* An error message was already printed */
276 exit(1);
277 }
278
279 if ( speedtest ) {
280 RunSpeedTest();
281 exit(0);
282 }
283
284 gRunning = true;
285 sound->PlaySound(gNovaBoom, 5);
286 screen->Fade(); /* Fade-out */
287 Delay(SOUND_DELAY);
288 gUpdateBuffer = true;
289 while ( sound->Playing() )
290 Delay(SOUND_DELAY);
291
292 while ( gRunning ) {
293
294 /* Update the screen if necessary */
295 if ( gUpdateBuffer )
296 DrawMainScreen();
297
298 /* -- Get an event */
299 screen->WaitEvent(&event);
300
301 /* -- Handle it! */
302 if ( event.type == SDL_KEYDOWN ) {
303 switch (event.key.keysym.sym) {
304
305 /* -- Toggle fullscreen */
306 case SDLK_RETURN:
307 if ( event.key.keysym.mod & KMOD_ALT )
308 screen->ToggleFullScreen();
309 break;
310
311 /* -- About the game...*/
312 case SDLK_a:
313 RunDoAbout();
314 break;
315
316 /* -- Configure the controls */
317 case SDLK_c:
318 RunConfigureControls();
319 break;
320
321 /* -- Start the game */
322 case SDLK_p:
323 RunPlayGame();
324 break;
325
326 /* -- Start the game */
327 case SDLK_l:
328 Delay(SOUND_DELAY);
329 sound->PlaySound(gLuckySound, 5);
330 gStartLevel = GetStartLevel();
331 if ( gStartLevel > 0 ) {
332 Delay(SOUND_DELAY);
333 sound->PlaySound(gNewLife, 5);
334 Delay(SOUND_DELAY);
335 NewGame();
336 }
337 break;
338
339 /* -- Let them leave */
340 case SDLK_q:
341 RunQuitGame();
342 break;
343
344 /* -- Set the volume */
345 /* (SDLK_0 - SDLK_8 are contiguous) */
346 case SDLK_0:
347 case SDLK_1:
348 case SDLK_2:
349 case SDLK_3:
350 case SDLK_4:
351 case SDLK_5:
352 case SDLK_6:
353 case SDLK_7:
354 case SDLK_8:
355 SetSoundLevel(event.key.keysym.sym
356 - SDLK_0);
357 break;
358
359 /* -- Give 'em a little taste of the peppers */
360 case SDLK_x:
361 Delay(SOUND_DELAY);
362 sound->PlaySound(gEnemyAppears, 5);
363 ShowDawn();
364 break;
365
366 /* -- Zap the high scores */
367 case SDLK_z:
368 RunZapScores();
369 break;
370
371 /* -- Create a screen dump of high scores */
372 case SDLK_F3:
373 screen->ScreenDump("ScoreDump",
374 64, 48, 298, 384);
375 break;
376
377 // Ignore Shift, Ctrl, Alt keys
378 case SDLK_LSHIFT:
379 case SDLK_RSHIFT:
380 case SDLK_LCTRL:
381 case SDLK_RCTRL:
382 case SDLK_LALT:
383 case SDLK_RALT:
384 break;
385
386 // Dink! :-)
387 default:
388 Delay(SOUND_DELAY);
389 sound->PlaySound(gSteelHit, 5);
390 break;
391 }
392 } else
393 /* -- Handle mouse clicks */
394 if ( event.type == SDL_MOUSEBUTTONDOWN ) {
395 buttons.Activate_Button(event.button.x,
396 event.button.y);
397 } else
398 /* -- Handle window close requests */
399 if ( event.type == SDL_QUIT ) {
400 RunQuitGame();
401 }
402 }
403 screen->Fade();
404 Delay(60);
405 return(0);
406 } /* -- main */
407
408
DrawText(int x,int y,char * text,MFont * font,Uint8 style,Uint8 R,Uint8 G,Uint8 B)409 int DrawText(int x, int y, char *text, MFont *font, Uint8 style,
410 Uint8 R, Uint8 G, Uint8 B)
411 {
412 SDL_Surface *textimage;
413 int width;
414
415 textimage = fontserv->TextImage(text, font, style, R, G, B);
416 if ( textimage == NULL ) {
417 width = 0;
418 } else {
419 screen->QueueBlit(x, y-textimage->h+2, textimage, NOCLIP);
420 width = textimage->w;
421 fontserv->FreeText(textimage);
422 }
423 return(width);
424 }
425
426
427 /* ----------------------------------------------------------------- */
428 /* -- Draw the current sound volume */
DrawSoundLevel(void)429 static void DrawSoundLevel(void)
430 {
431 static int need_init=1;
432 static MFont *geneva;
433 static char text[12];
434 static int xOff, yOff;
435
436 if ( need_init ) {
437 if ( (geneva = fontserv->NewFont("Geneva", 9)) == NULL ) {
438 error("Can't use Geneva font! -- Exiting.\n");
439 exit(255);
440 }
441 xOff = (SCREEN_WIDTH - 512) / 2;
442 yOff = (SCREEN_HEIGHT - 384) / 2;
443 need_init = 0;
444 } else {
445 DrawText(xOff+309-7, yOff+240-6, text, geneva, STYLE_BOLD,
446 0x00, 0x00, 0x00);
447 }
448 snprintf(text, sizeof(text), "%d", gSoundLevel);
449 DrawText(xOff+309-7, yOff+240-6, text, geneva, STYLE_BOLD,
450 30000>>8, 30000>>8, 0xFF);
451 screen->Update();
452 } /* -- DrawSoundLevel */
453
454
455 /* ----------------------------------------------------------------- */
456 /* -- Draw the main screen */
457
DrawMainScreen(void)458 void DrawMainScreen(void)
459 {
460 SDL_Surface *title;
461 MFont *font, *bigfont;
462 MPoint pt;
463 Uint16 width, height;
464 Uint16 xOff, yOff, botDiv, rightDiv;
465 Uint16 index, sRt, wRt, sw;
466 Uint32 clr, ltClr, ltrClr;
467 char buffer[128];
468 int offset;
469
470 gUpdateBuffer = false;
471 buttons.Delete_Buttons();
472
473 width = 512;
474 height = 384;
475 xOff = (SCREEN_WIDTH - width) / 2;
476 yOff = (SCREEN_HEIGHT - height) / 2;
477
478 title = Load_Title(screen, 129);
479 if ( title == NULL ) {
480 error("Can't load 'title' title! (ID=%d)\n", 129);
481 exit(255);
482 }
483
484 clr = screen->MapRGB(30000>>8, 30000>>8, 0xFF);
485 ltClr = screen->MapRGB(40000>>8, 40000>>8, 0xFF);
486 ltrClr = screen->MapRGB(50000>>8, 50000>>8, 0xFF);
487
488 screen->Lock();
489 screen->Clear();
490 /* -- Draw the screen frame */
491 screen->DrawRect(xOff-1, yOff-1, width+2, height+2, clr);
492 screen->DrawRect(xOff-2, yOff-2, width+4, height+4, clr);
493 screen->DrawRect(xOff-3, yOff-3, width+6, height+6, ltClr);
494 screen->DrawRect(xOff-4, yOff-4, width+8, height+8, ltClr);
495 screen->DrawRect(xOff-5, yOff-5, width+10, height+10, ltrClr);
496 screen->DrawRect(xOff-6, yOff-6, width+12, height+12, ltClr);
497 screen->DrawRect(xOff-7, yOff-7, width+14, height+14, clr);
498 /* -- Draw the dividers */
499 botDiv = yOff + 5 + title->h + 5;
500 rightDiv = xOff + 5 + title->w + 5;
501 screen->DrawLine(rightDiv, yOff, rightDiv, yOff+height, ltClr);
502 screen->DrawLine(xOff, botDiv, rightDiv, botDiv, ltClr);
503 screen->DrawLine(rightDiv, 263+yOff, xOff+width, 263+yOff, ltClr);
504 /* -- Draw the title image */
505 screen->Unlock();
506 screen->QueueBlit(xOff+5, yOff+5, title, NOCLIP);
507 screen->Update();
508 screen->FreeImage(title);
509
510
511 /* -- Draw the high scores */
512
513 /* -- First the headings -- fontserv() isn't elegant, but hey.. */
514 if ( (bigfont = fontserv->NewFont("New York", 18)) == NULL ) {
515 error("Can't use New York (18) font! -- Exiting.\n");
516 exit(255);
517 }
518 DrawText(xOff+5, botDiv+22, "Name", bigfont, STYLE_ULINE,
519 0xFF, 0xFF, 0x00);
520 sRt = xOff+185;
521 DrawText(sRt, botDiv+22, "Score", bigfont, STYLE_ULINE,
522 0xFF, 0xFF, 0x00);
523 sRt += fontserv->TextWidth("Score", bigfont, STYLE_ULINE);
524 wRt = xOff+245;
525 DrawText(wRt, botDiv+22, "Wave", bigfont, STYLE_ULINE,
526 0xFF, 0xFF, 0x00);
527 wRt += fontserv->TextWidth("Wave", bigfont, STYLE_ULINE)-10;
528
529 /* -- Now the scores */
530 LoadScores();
531 if ( (font = fontserv->NewFont("New York", 14)) == NULL ) {
532 error("Can't use New York (14) font! -- Exiting.\n");
533 exit(255);
534 }
535
536 for (index = 0; index < 10; index++) {
537 Uint8 R, G, B;
538
539 if ( gLastHigh == index ) {
540 R = 0xFF;
541 G = 0xFF;
542 B = 0xFF;
543 } else {
544 R = 30000>>8;
545 G = 30000>>8;
546 B = 30000>>8;
547 }
548 DrawText(xOff+5, botDiv+42+(index*18), hScores[index].name,
549 font, STYLE_BOLD, R, G, B);
550 snprintf(buffer, sizeof(buffer), "%u", hScores[index].score);
551 sw = fontserv->TextWidth(buffer, font, STYLE_BOLD);
552 DrawText(sRt-sw, botDiv+42+(index*18), buffer,
553 font, STYLE_BOLD, R, G, B);
554 snprintf(buffer, sizeof(buffer), "%u", hScores[index].wave);
555 sw = fontserv->TextWidth(buffer, font, STYLE_BOLD);
556 DrawText(wRt-sw, botDiv+42+(index*18), buffer,
557 font, STYLE_BOLD, R, G, B);
558 }
559 delete font;
560
561 DrawText(xOff+5, botDiv+46+(10*18)+3, "Last Score: ",
562 bigfont, STYLE_NORM, 0xFF, 0xFF, 0xFF);
563 snprintf(buffer, sizeof(buffer), "%d", GetScore());
564 sw = fontserv->TextWidth("Last Score: ", bigfont, STYLE_NORM);
565 DrawText(xOff+5+sw, botDiv+46+(index*18)+3, buffer,
566 bigfont, STYLE_NORM, 0xFF, 0xFF, 0xFF);
567 delete bigfont;
568
569 /* -- Draw the Instructions */
570 offset = 34;
571
572 pt.h = rightDiv + 10;
573 pt.v = yOff + 10;
574 DrawKey(&pt, "P", " Start playing Maelstrom", RunPlayGame);
575
576 pt.h = rightDiv + 10;
577 pt.v += offset;
578 DrawKey(&pt, "C", " Configure the game controls", RunConfigureControls);
579
580 pt.h = rightDiv + 10;
581 pt.v += offset;
582 DrawKey(&pt, "Z", " Zap the high scores", RunZapScores);
583
584 pt.h = rightDiv + 10;
585 pt.v += offset;
586 DrawKey(&pt, "A", " About Maelstrom...", RunDoAbout);
587
588 pt.v += offset;
589
590 pt.h = rightDiv + 10;
591 pt.v += offset;
592 DrawKey(&pt, "Q", " Quit Maelstrom", RunQuitGame);
593
594 pt.h = rightDiv + 10;
595 pt.v += offset;
596 DrawKey(&pt, "0", " ", DecrementSound);
597
598 if ( (font = fontserv->NewFont("Geneva", 9)) == NULL ) {
599 error("Can't use Geneva font! -- Exiting.\n");
600 exit(255);
601 }
602 DrawText(pt.h+gKeyIcon->w+3, pt.v+19, "-",
603 font, STYLE_NORM, 0xFF, 0xFF, 0x00);
604
605 pt.h = rightDiv + 50;
606 DrawKey(&pt, "8", " Set Sound Volume", IncrementSound);
607
608 /* -- Draw the credits */
609
610 DrawText(xOff+5+68, yOff+5+127, "Port to Linux by Sam Lantinga",
611 font, STYLE_BOLD, 0xFF, 0xFF, 0x00);
612 DrawText(rightDiv+10, yOff+259, "�1992-4 Ambrosia Software, Inc.",
613 font, STYLE_BOLD, 0xFF, 0xFF, 0xFF);
614
615 /* -- Draw the version number */
616
617 DrawText(xOff+20, yOff+151, VERSION_STRING,
618 font, STYLE_NORM, 0xFF, 0xFF, 0xFF);
619 delete font;
620
621 DrawSoundLevel();
622
623 /* Always drawing while faded out -- fade in */
624 screen->Update();
625 screen->Fade();
626 } /* -- DrawMainScreen */
627
628
629
630 /* ----------------------------------------------------------------- */
631 /* -- Draw the key and its function */
632
DrawKey(MPoint * pt,char * key,char * text,void (* callback)(void))633 static void DrawKey(MPoint *pt, char *key, char *text, void (*callback)(void))
634 {
635 MFont *geneva;
636
637 if ( (geneva = fontserv->NewFont("Geneva", 9)) == NULL ) {
638 error("Can't use Geneva font! -- Exiting.\n");
639 exit(255);
640 }
641 screen->QueueBlit(pt->h, pt->v, gKeyIcon);
642 screen->Update();
643
644 DrawText(pt->h+14, pt->v+20, key, geneva, STYLE_BOLD, 0xFF, 0xFF, 0xFF);
645 DrawText(pt->h+13, pt->v+19, key, geneva, STYLE_BOLD, 0x00, 0x00, 0x00);
646 DrawText(pt->h+gKeyIcon->w+3, pt->v+19, text,
647 geneva, STYLE_BOLD, 0xFF, 0xFF, 0x00);
648 delete geneva;
649
650 buttons.Add_Button(pt->h, pt->v, gKeyIcon->w, gKeyIcon->h, callback);
651 } /* -- DrawKey */
652
653
Message(char * message)654 void Message(char *message)
655 {
656 static MFont *font;
657 static int xOff;
658 static char *last_message;
659
660 if ( ! last_message ) { /* Initialize everything */
661 /* This was taken from the DrawMainScreen function */
662 xOff = (SCREEN_WIDTH - 512) / 2;
663
664 if ( (font = fontserv->NewFont("New York", 14)) == NULL ) {
665 error("Can't use New York(14) font! -- Exiting.\n");
666 exit(255);
667 }
668 } else {
669 DrawText(xOff, 25, last_message, font, STYLE_BOLD, 0, 0, 0);
670 delete[] last_message;
671 }
672 if ( message ) {
673 DrawText(xOff, 25, message, font, STYLE_BOLD, 0xCC,0xCC,0xCC);
674 last_message = new char[strlen(message)+1];
675 strcpy(last_message, message);
676 } else {
677 last_message = new char[1];
678 last_message[0] = '\0';
679 }
680 screen->Update();
681 }
682
683