/* ------------------------------------------------------------- */ /* */ /* Maelstrom */ /* By Andrew Welch */ /* */ /* Ported to Linux (Spring 1995) */ /* Ported to Win95 (Fall 1996) -- not releasable */ /* Ported to SDL (Fall 1997) */ /* By Sam Lantinga (slouken@devolution.com) */ /* */ /* ------------------------------------------------------------- */ #include "Maelstrom_Globals.h" #include "buttonlist.h" #include "load.h" #include "fastrand.h" #include "checksum.h" /* External functions used in this file */ extern int DoInitializations(Uint32 video_flags); /* init.cc */ static char *Version = "Maelstrom v1.4.3 (GPL version 3.0.6) -- 10/19/2002 by Sam Lantinga\n"; // Global variables set in this file... int gStartLives; int gStartLevel; Bool gUpdateBuffer; Bool gRunning; int gNoDelay; // Local variables in this file... static ButtonList buttons; // Local functions in this file... static void DrawMainScreen(void); static void DrawSoundLevel(void); static void DrawKey(MPoint *pt, char *ch, char *str, void (*callback)(void)); // Main Menu actions: static void RunDoAbout(void) { gNoDelay = 0; Delay(SOUND_DELAY); sound->PlaySound(gNovaAppears, 5); DoAbout(); } static void RunConfigureControls(void) { Delay(SOUND_DELAY); sound->PlaySound(gHomingAppears, 5); ConfigureControls(); } static void RunPlayGame(void) { gStartLives = 3; gStartLevel = 1; gNoDelay = 0; sound->PlaySound(gNewLife, 5); Delay(SOUND_DELAY); NewGame(); Message(NULL); /* Clear any messages */ } static void RunQuitGame(void) { Delay(SOUND_DELAY); sound->PlaySound(gMultiplierGone, 5); while ( sound->Playing() ) Delay(SOUND_DELAY); gRunning = false; } static void IncrementSound(void) { if ( gSoundLevel < 8 ) { sound->Volume(++gSoundLevel); sound->PlaySound(gNewLife, 5); /* -- Draw the new sound level */ DrawSoundLevel(); } } static void DecrementSound(void) { if ( gSoundLevel > 0 ) { sound->Volume(--gSoundLevel); sound->PlaySound(gNewLife, 5); /* -- Draw the new sound level */ DrawSoundLevel(); } } static void SetSoundLevel(int volume) { /* Make sure the device is working */ sound->Volume(volume); /* Set the new sound level! */ gSoundLevel = volume; sound->PlaySound(gNewLife, 5); /* -- Draw the new sound level */ DrawSoundLevel(); } static void RunZapScores(void) { Delay(SOUND_DELAY); sound->PlaySound(gMultShotSound, 5); if ( ZapHighScores() ) { /* Fade the screen and redisplay scores */ screen->Fade(); Delay(SOUND_DELAY); sound->PlaySound(gExplosionSound, 5); gUpdateBuffer = true; } } /* ----------------------------------------------------------------- */ /* -- Run a graphics speed test. */ static void RunSpeedTest(void) { const int test_reps = 100; /* How many full cycles to run */ Uint32 then, now; int i, frame, x=((640/2)-16), y=((480/2)-16), onscreen=0; screen->Clear(); then = SDL_GetTicks(); for ( i=0; iClear(x, y, 32, 32); } else { onscreen = 1; } screen->QueueBlit(x, y, gPlayerShip->sprite[frame]); screen->Update(); } } now = SDL_GetTicks(); mesg("Graphics speed test took %d microseconds per cycle.\r\n", ((now-then)/test_reps)); } /* ----------------------------------------------------------------- */ /* -- Print a Usage message and quit. In several places we depend on this function exiting. */ static char *progname; void PrintUsage(void) { error("\nUsage: %s [-netscores] -printscores\n", progname); error("or\n"); error("Usage: %s \n\n", progname); error("Where can be any of:\n\n" " -fullscreen # Run Maelstrom in full-screen mode\n" " -gamma [0-8] # Set the gamma correction\n" " -volume [0-8] # Set the sound volume\n" " -netscores # Use the world-wide network score server\n" ); LogicUsage(); error("\n"); exit(1); } /* ----------------------------------------------------------------- */ /* -- Blitter main program */ int main(int argc, char *argv[]) { /* Command line flags */ int doprinthigh = 0; int speedtest = 0; Uint32 video_flags = SDL_SWSURFACE; /* Normal variables */ SDL_Event event; LibPath::SetExePath(argv[0]); #ifndef __WIN95__ /* The first thing we do is calculate our checksum */ (void) checksum(); #endif /* ! Win95 */ /* Seed the random number generator */ SeedRandom(0L); /* Initialize the controls */ LoadControls(); /* Initialize game logic data structures */ if ( InitLogicData() < 0 ) { exit(1); } /* Parse command line arguments */ for ( progname=argv[0]; --argc; ++argv ) { if ( strcmp(argv[1], "-fullscreen") == 0 ) { video_flags |= SDL_FULLSCREEN; } else if ( strcmp(argv[1], "-gamma") == 0 ) { int gammacorrect; if ( ! argv[2] ) { /* Print the current gamma */ mesg("Current Gamma correction level: %d\n", gGammaCorrect); exit(0); } if ( (gammacorrect=atoi(argv[2])) < 0 || gammacorrect > 8 ) { error( "Gamma correction value must be between 0 and 8. -- Exiting.\n"); exit(1); } /* We need to update the gamma */ gGammaCorrect = gammacorrect; SaveControls(); ++argv; --argc; } else if ( strcmp(argv[1], "-volume") == 0 ) { int volume; if ( ! argv[2] ) { /* Print the current volume */ mesg("Current volume level: %d\n", gSoundLevel); exit(0); } if ( (volume=atoi(argv[2])) < 0 || volume > 8 ) { error( "Volume must be a number between 0 and 8. -- Exiting.\n"); exit(1); } /* We need to update the volume */ gSoundLevel = volume; SaveControls(); ++argv; --argc; } #define CHECKSUM_DEBUG #ifdef CHECKSUM_DEBUG else if ( strcmp(argv[1], "-checksum") == 0 ) { mesg("Checksum = %s\n", get_checksum(NULL, 0)); exit(0); } #endif /* CHECKSUM_DEBUG */ else if ( strcmp(argv[1], "-printscores") == 0 ) doprinthigh = 1; else if ( strcmp(argv[1], "-netscores") == 0 ) gNetScores = 1; else if ( strcmp(argv[1], "-speedtest") == 0 ) speedtest = 1; else if ( LogicParseArgs(&argv, &argc) == 0 ) { /* LogicParseArgs() took care of everything */; } else if ( strcmp(argv[1], "-version") == 0 ) { error("%s", Version); exit(0); } else { PrintUsage(); } } /* Do we just want the high scores? */ if ( doprinthigh ) { PrintHighScores(); exit(0); } /* Make sure we have a valid player list (netlogic) */ if ( InitLogic() < 0 ) exit(1); /* Initialize everything. :) */ if ( DoInitializations(video_flags) < 0 ) { /* An error message was already printed */ exit(1); } if ( speedtest ) { RunSpeedTest(); exit(0); } gRunning = true; sound->PlaySound(gNovaBoom, 5); screen->Fade(); /* Fade-out */ Delay(SOUND_DELAY); gUpdateBuffer = true; while ( sound->Playing() ) Delay(SOUND_DELAY); while ( gRunning ) { /* Update the screen if necessary */ if ( gUpdateBuffer ) DrawMainScreen(); /* -- Get an event */ screen->WaitEvent(&event); /* -- Handle it! */ if ( event.type == SDL_KEYDOWN ) { switch (event.key.keysym.sym) { /* -- Toggle fullscreen */ case SDLK_RETURN: if ( event.key.keysym.mod & KMOD_ALT ) screen->ToggleFullScreen(); break; /* -- About the game...*/ case SDLK_a: RunDoAbout(); break; /* -- Configure the controls */ case SDLK_c: RunConfigureControls(); break; /* -- Start the game */ case SDLK_p: RunPlayGame(); break; /* -- Start the game */ case SDLK_l: Delay(SOUND_DELAY); sound->PlaySound(gLuckySound, 5); gStartLevel = GetStartLevel(); if ( gStartLevel > 0 ) { Delay(SOUND_DELAY); sound->PlaySound(gNewLife, 5); Delay(SOUND_DELAY); NewGame(); } break; /* -- Let them leave */ case SDLK_q: RunQuitGame(); break; /* -- Set the volume */ /* (SDLK_0 - SDLK_8 are contiguous) */ case SDLK_0: case SDLK_1: case SDLK_2: case SDLK_3: case SDLK_4: case SDLK_5: case SDLK_6: case SDLK_7: case SDLK_8: SetSoundLevel(event.key.keysym.sym - SDLK_0); break; /* -- Give 'em a little taste of the peppers */ case SDLK_x: Delay(SOUND_DELAY); sound->PlaySound(gEnemyAppears, 5); ShowDawn(); break; /* -- Zap the high scores */ case SDLK_z: RunZapScores(); break; /* -- Create a screen dump of high scores */ case SDLK_F3: screen->ScreenDump("ScoreDump", 64, 48, 298, 384); break; // Ignore Shift, Ctrl, Alt keys case SDLK_LSHIFT: case SDLK_RSHIFT: case SDLK_LCTRL: case SDLK_RCTRL: case SDLK_LALT: case SDLK_RALT: break; // Dink! :-) default: Delay(SOUND_DELAY); sound->PlaySound(gSteelHit, 5); break; } } else /* -- Handle mouse clicks */ if ( event.type == SDL_MOUSEBUTTONDOWN ) { buttons.Activate_Button(event.button.x, event.button.y); } else /* -- Handle window close requests */ if ( event.type == SDL_QUIT ) { RunQuitGame(); } } screen->Fade(); Delay(60); return(0); } /* -- main */ int DrawText(int x, int y, char *text, MFont *font, Uint8 style, Uint8 R, Uint8 G, Uint8 B) { SDL_Surface *textimage; int width; textimage = fontserv->TextImage(text, font, style, R, G, B); if ( textimage == NULL ) { width = 0; } else { screen->QueueBlit(x, y-textimage->h+2, textimage, NOCLIP); width = textimage->w; fontserv->FreeText(textimage); } return(width); } /* ----------------------------------------------------------------- */ /* -- Draw the current sound volume */ static void DrawSoundLevel(void) { static int need_init=1; static MFont *geneva; static char text[12]; static int xOff, yOff; if ( need_init ) { if ( (geneva = fontserv->NewFont("Geneva", 9)) == NULL ) { error("Can't use Geneva font! -- Exiting.\n"); exit(255); } xOff = (SCREEN_WIDTH - 512) / 2; yOff = (SCREEN_HEIGHT - 384) / 2; need_init = 0; } else { DrawText(xOff+309-7, yOff+240-6, text, geneva, STYLE_BOLD, 0x00, 0x00, 0x00); } snprintf(text, sizeof(text), "%d", gSoundLevel); DrawText(xOff+309-7, yOff+240-6, text, geneva, STYLE_BOLD, 30000>>8, 30000>>8, 0xFF); screen->Update(); } /* -- DrawSoundLevel */ /* ----------------------------------------------------------------- */ /* -- Draw the main screen */ void DrawMainScreen(void) { SDL_Surface *title; MFont *font, *bigfont; MPoint pt; Uint16 width, height; Uint16 xOff, yOff, botDiv, rightDiv; Uint16 index, sRt, wRt, sw; Uint32 clr, ltClr, ltrClr; char buffer[128]; int offset; gUpdateBuffer = false; buttons.Delete_Buttons(); width = 512; height = 384; xOff = (SCREEN_WIDTH - width) / 2; yOff = (SCREEN_HEIGHT - height) / 2; title = Load_Title(screen, 129); if ( title == NULL ) { error("Can't load 'title' title! (ID=%d)\n", 129); exit(255); } clr = screen->MapRGB(30000>>8, 30000>>8, 0xFF); ltClr = screen->MapRGB(40000>>8, 40000>>8, 0xFF); ltrClr = screen->MapRGB(50000>>8, 50000>>8, 0xFF); screen->Lock(); screen->Clear(); /* -- Draw the screen frame */ screen->DrawRect(xOff-1, yOff-1, width+2, height+2, clr); screen->DrawRect(xOff-2, yOff-2, width+4, height+4, clr); screen->DrawRect(xOff-3, yOff-3, width+6, height+6, ltClr); screen->DrawRect(xOff-4, yOff-4, width+8, height+8, ltClr); screen->DrawRect(xOff-5, yOff-5, width+10, height+10, ltrClr); screen->DrawRect(xOff-6, yOff-6, width+12, height+12, ltClr); screen->DrawRect(xOff-7, yOff-7, width+14, height+14, clr); /* -- Draw the dividers */ botDiv = yOff + 5 + title->h + 5; rightDiv = xOff + 5 + title->w + 5; screen->DrawLine(rightDiv, yOff, rightDiv, yOff+height, ltClr); screen->DrawLine(xOff, botDiv, rightDiv, botDiv, ltClr); screen->DrawLine(rightDiv, 263+yOff, xOff+width, 263+yOff, ltClr); /* -- Draw the title image */ screen->Unlock(); screen->QueueBlit(xOff+5, yOff+5, title, NOCLIP); screen->Update(); screen->FreeImage(title); /* -- Draw the high scores */ /* -- First the headings -- fontserv() isn't elegant, but hey.. */ if ( (bigfont = fontserv->NewFont("New York", 18)) == NULL ) { error("Can't use New York (18) font! -- Exiting.\n"); exit(255); } DrawText(xOff+5, botDiv+22, "Name", bigfont, STYLE_ULINE, 0xFF, 0xFF, 0x00); sRt = xOff+185; DrawText(sRt, botDiv+22, "Score", bigfont, STYLE_ULINE, 0xFF, 0xFF, 0x00); sRt += fontserv->TextWidth("Score", bigfont, STYLE_ULINE); wRt = xOff+245; DrawText(wRt, botDiv+22, "Wave", bigfont, STYLE_ULINE, 0xFF, 0xFF, 0x00); wRt += fontserv->TextWidth("Wave", bigfont, STYLE_ULINE)-10; /* -- Now the scores */ LoadScores(); if ( (font = fontserv->NewFont("New York", 14)) == NULL ) { error("Can't use New York (14) font! -- Exiting.\n"); exit(255); } for (index = 0; index < 10; index++) { Uint8 R, G, B; if ( gLastHigh == index ) { R = 0xFF; G = 0xFF; B = 0xFF; } else { R = 30000>>8; G = 30000>>8; B = 30000>>8; } DrawText(xOff+5, botDiv+42+(index*18), hScores[index].name, font, STYLE_BOLD, R, G, B); snprintf(buffer, sizeof(buffer), "%u", hScores[index].score); sw = fontserv->TextWidth(buffer, font, STYLE_BOLD); DrawText(sRt-sw, botDiv+42+(index*18), buffer, font, STYLE_BOLD, R, G, B); snprintf(buffer, sizeof(buffer), "%u", hScores[index].wave); sw = fontserv->TextWidth(buffer, font, STYLE_BOLD); DrawText(wRt-sw, botDiv+42+(index*18), buffer, font, STYLE_BOLD, R, G, B); } delete font; DrawText(xOff+5, botDiv+46+(10*18)+3, "Last Score: ", bigfont, STYLE_NORM, 0xFF, 0xFF, 0xFF); snprintf(buffer, sizeof(buffer), "%d", GetScore()); sw = fontserv->TextWidth("Last Score: ", bigfont, STYLE_NORM); DrawText(xOff+5+sw, botDiv+46+(index*18)+3, buffer, bigfont, STYLE_NORM, 0xFF, 0xFF, 0xFF); delete bigfont; /* -- Draw the Instructions */ offset = 34; pt.h = rightDiv + 10; pt.v = yOff + 10; DrawKey(&pt, "P", " Start playing Maelstrom", RunPlayGame); pt.h = rightDiv + 10; pt.v += offset; DrawKey(&pt, "C", " Configure the game controls", RunConfigureControls); pt.h = rightDiv + 10; pt.v += offset; DrawKey(&pt, "Z", " Zap the high scores", RunZapScores); pt.h = rightDiv + 10; pt.v += offset; DrawKey(&pt, "A", " About Maelstrom...", RunDoAbout); pt.v += offset; pt.h = rightDiv + 10; pt.v += offset; DrawKey(&pt, "Q", " Quit Maelstrom", RunQuitGame); pt.h = rightDiv + 10; pt.v += offset; DrawKey(&pt, "0", " ", DecrementSound); if ( (font = fontserv->NewFont("Geneva", 9)) == NULL ) { error("Can't use Geneva font! -- Exiting.\n"); exit(255); } DrawText(pt.h+gKeyIcon->w+3, pt.v+19, "-", font, STYLE_NORM, 0xFF, 0xFF, 0x00); pt.h = rightDiv + 50; DrawKey(&pt, "8", " Set Sound Volume", IncrementSound); /* -- Draw the credits */ DrawText(xOff+5+68, yOff+5+127, "Port to Linux by Sam Lantinga", font, STYLE_BOLD, 0xFF, 0xFF, 0x00); DrawText(rightDiv+10, yOff+259, "©1992-4 Ambrosia Software, Inc.", font, STYLE_BOLD, 0xFF, 0xFF, 0xFF); /* -- Draw the version number */ DrawText(xOff+20, yOff+151, VERSION_STRING, font, STYLE_NORM, 0xFF, 0xFF, 0xFF); delete font; DrawSoundLevel(); /* Always drawing while faded out -- fade in */ screen->Update(); screen->Fade(); } /* -- DrawMainScreen */ /* ----------------------------------------------------------------- */ /* -- Draw the key and its function */ static void DrawKey(MPoint *pt, char *key, char *text, void (*callback)(void)) { MFont *geneva; if ( (geneva = fontserv->NewFont("Geneva", 9)) == NULL ) { error("Can't use Geneva font! -- Exiting.\n"); exit(255); } screen->QueueBlit(pt->h, pt->v, gKeyIcon); screen->Update(); DrawText(pt->h+14, pt->v+20, key, geneva, STYLE_BOLD, 0xFF, 0xFF, 0xFF); DrawText(pt->h+13, pt->v+19, key, geneva, STYLE_BOLD, 0x00, 0x00, 0x00); DrawText(pt->h+gKeyIcon->w+3, pt->v+19, text, geneva, STYLE_BOLD, 0xFF, 0xFF, 0x00); delete geneva; buttons.Add_Button(pt->h, pt->v, gKeyIcon->w, gKeyIcon->h, callback); } /* -- DrawKey */ void Message(char *message) { static MFont *font; static int xOff; static char *last_message; if ( ! last_message ) { /* Initialize everything */ /* This was taken from the DrawMainScreen function */ xOff = (SCREEN_WIDTH - 512) / 2; if ( (font = fontserv->NewFont("New York", 14)) == NULL ) { error("Can't use New York(14) font! -- Exiting.\n"); exit(255); } } else { DrawText(xOff, 25, last_message, font, STYLE_BOLD, 0, 0, 0); delete[] last_message; } if ( message ) { DrawText(xOff, 25, message, font, STYLE_BOLD, 0xCC,0xCC,0xCC); last_message = new char[strlen(message)+1]; strcpy(last_message, message); } else { last_message = new char[1]; last_message[0] = '\0'; } screen->Update(); }