/* ALIENS: A silly little game demonstrating the SDL and mixer libraries Copyright (C) 1998 Sam Lantinga Modified code to use resources packed in the game file. Copyright (C) 2006 Xavier Garreau This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Sam Lantinga 5635-34 Springhouse Dr. Pleasanton, CA 94588 (USA) slouken@devolution.com */ #include #include #include #include #include #include #include #include "SDL.h" #include "SDL_mixer.h" #include "SDL_image.h" #ifdef macintosh #define DIR_SEP ":" #define DIR_CUR ":" #else #define DIR_SEP "/" #define DIR_CUR "" #endif #define DATAFILE(X) DIR_CUR "data" DIR_SEP X #define FRAMES_PER_SEC 50 #define PLAYER_SPEED 4 #define MAX_SHOTS 3 #define SHOT_SPEED 6 #define MAX_ALIENS 30 #define ALIEN_SPEED 5 #define ALIEN_ODDS (1*FRAMES_PER_SEC) #define EXPLODE_TIME 4 const char CHECK_SUFFIX[] = "SDLALLINONE"; const long suffix_len = sizeof(CHECK_SUFFIX); const long l_size = sizeof(long); SDL_RWops *SAIO_GetResource (const char* resFile, const char* resPath, char** donnees) { int us_fd; long taille_donnees = 0; long nb_couples; long i; long taille_nom = 0; long taille = 0; char* nom; char buffer[suffix_len]; us_fd = open (resFile, O_RDONLY); if (-1 == us_fd) { return NULL; } // On s'est auto ouvert. Cool :) // On vérifie le suffixe lseek (us_fd, -suffix_len, SEEK_END); memset (buffer, 0, suffix_len); read (us_fd, buffer, suffix_len); if (0 != strcmp(buffer, CHECK_SUFFIX)) { // On n'a pas le suffixe, il n'y a donc pas de pack // On tente la fonction "normale" close (us_fd); fprintf (stderr, "Attention: %s n'est pas une appli SAIO\n", resFile); return NULL; } lseek (us_fd, -suffix_len, SEEK_CUR); // Si on arrive là, on a le suffixe en fin de fichier. // On peut récupérer le nombre de données packées lseek (us_fd, -l_size, SEEK_CUR); read (us_fd, &nb_couples, l_size); if (0 == nb_couples) { fprintf (stderr, "Attention: %s ne contient pas de resources\n", resFile); return NULL; } lseek (us_fd, -l_size, SEEK_CUR); // On remonte dans les ressources i = nb_couples-1; nom = NULL; *donnees = NULL; do { lseek (us_fd, -l_size, SEEK_CUR); read (us_fd, &taille, l_size); lseek (us_fd, -l_size, SEEK_CUR); if (i%2) { //données taille_donnees = taille; } else { // nom taille_nom = taille; lseek (us_fd, -taille, SEEK_CUR); nom = (char*)realloc (nom, taille+1); if (nom) { memset (nom, 0, taille+1); read (us_fd, nom, taille); if (!strcmp (resPath, nom)) { lseek (us_fd, l_size, SEEK_CUR); *donnees = (char*)malloc (taille_donnees); if (*donnees) { read (us_fd, *donnees, taille_donnees); } break; } } } lseek (us_fd, -taille, SEEK_CUR); } while (i--); free (nom); if (!*donnees) { fprintf (stderr, "Attention: %s ne contient pas %s\n", resFile, resPath); return NULL; } close (us_fd); return SDL_RWFromMem(*donnees, taille_donnees); } Mix_Chunk* SAIO_Mix_LoadWAV (const char* resFile, const char* resPath) { Mix_Chunk* sdl_sound = NULL; SDL_RWops *rw; char* donnees = NULL; rw = SAIO_GetResource (resFile, resPath, &donnees); if (!rw) { // On essaye à l'ancienne return Mix_LoadWAV (resPath); } sdl_sound = Mix_LoadWAV_RW(rw, 1); free (donnees); return sdl_sound; } Mix_Music* SAIO_Mix_LoadMUS (const char* resFile, const char* resPath) { Mix_Music* sdl_music = NULL; SDL_RWops *rw; char* donnees = NULL; rw = SAIO_GetResource (resFile, resPath, &donnees); if (!rw) { // On essaye à l'ancienne return Mix_LoadMUS (resPath); } sdl_music = Mix_LoadMUS_RW(rw); free (donnees); SDL_FreeRW (rw); return sdl_music; } SDL_Surface* SAIO_IMG_Load (const char* resFile, const char* resPath) { SDL_Surface* sdl_surf = NULL; SDL_RWops *rw; char* donnees = NULL; rw = SAIO_GetResource (resFile, resPath, &donnees); if (!rw) { // On essaye à l'ancienne return IMG_Load (resPath); } sdl_surf = IMG_Load_RW(rw, 1); free (donnees); return sdl_surf; } // Code original: typedef struct { int alive; int facing; int x, y; SDL_Surface *image; } object; /**/ SDL_Surface *screen; SDL_Surface *background; /**/ object player; int reloading; object shots[MAX_SHOTS]; /**/ object aliens[MAX_ALIENS]; /**/ object explosions[MAX_ALIENS+1]; /**/ #define MAX_UPDATES 3*(1+MAX_SHOTS+MAX_ALIENS) int numupdates; SDL_Rect srcupdate[MAX_UPDATES]; SDL_Rect dstupdate[MAX_UPDATES]; struct blit { SDL_Surface *src; SDL_Rect *srcrect; SDL_Rect *dstrect; } blits[MAX_UPDATES]; /**/ #if defined(PLAY_MOD) || defined(PLAY_MID) Mix_Music *music; #endif enum { MUSIC_WAV, SHOT_WAV, EXPLODE_WAV, NUM_WAVES }; Mix_Chunk *sounds[NUM_WAVES]; SDL_Surface *LoadImage(const char* resFile, char *resPath, int transparent) { SDL_Surface *image, *surface; image = SAIO_IMG_Load(resFile, resPath); if ( image == NULL ) { fprintf(stderr, "Couldn't load image %s: %s\n", resPath, IMG_GetError()); return(NULL); } if ( transparent ) { /* Assuming 8-bit BMP image */ SDL_SetColorKey(image, (SDL_SRCCOLORKEY|SDL_RLEACCEL), *(Uint8 *)image->pixels); } surface = SDL_DisplayFormat(image); SDL_FreeSurface(image); return(surface); } int LoadData(const char* resFile) { int i; /* Load sounds */ #if defined(PLAY_MOD) || defined(PLAY_MID) #if defined(PLAY_MOD) music = SAIO_Mix_LoadMUS(resFile, DATAFILE("music.it")); #elif defined(PLAY_MID) music = SAIO_Mix_LoadMUS(resFile, DATAFILE("music.mid")); #endif if ( music == NULL ) { fprintf(stderr, "Warning: Couldn't load music: %s\n", Mix_GetError()); } #else sounds[MUSIC_WAV] = SAIO_Mix_LoadWAV(resFile, DATAFILE("music.wav")); #endif sounds[SHOT_WAV] = SAIO_Mix_LoadWAV(resFile, DATAFILE("shot.wav")); sounds[EXPLODE_WAV] = SAIO_Mix_LoadWAV(resFile, DATAFILE("explode.wav")); /* Load graphics */ player.image = LoadImage(resFile, DATAFILE("player.gif"), 1); if ( player.image == NULL ) { return(0); } shots[0].image = LoadImage(resFile, DATAFILE("shot.gif"), 0); if ( shots[0].image == NULL ) { return(0); } for ( i=1; iw-aliens[i].image->w-1; } else { aliens[i].x = 0; } aliens[i].alive = 1; } void DrawObject(object *sprite) { struct blit *update; update = &blits[numupdates++]; update->src = sprite->image; update->srcrect->x = 0; update->srcrect->y = 0; update->srcrect->w = sprite->image->w; update->srcrect->h = sprite->image->h; update->dstrect->x = sprite->x; update->dstrect->y = sprite->y; update->dstrect->w = sprite->image->w; update->dstrect->h = sprite->image->h; } void EraseObject(object *sprite) { struct blit *update; int wrap; /* The background wraps horizontally across the screen */ update = &blits[numupdates++]; update->src = background; update->srcrect->x = sprite->x%background->w; update->srcrect->y = sprite->y; update->srcrect->w = sprite->image->w; update->srcrect->h = sprite->image->h; wrap = (update->srcrect->x+update->srcrect->w)-(background->w); if ( wrap > 0 ) { update->srcrect->w -= wrap; } update->dstrect->x = sprite->x; update->dstrect->y = sprite->y; update->dstrect->w = update->srcrect->w; update->dstrect->h = update->srcrect->h; /* Assuming sprites can only wrap across one background tile */ if ( wrap > 0 ) { update = &blits[numupdates++]; update->src = background; update->srcrect->x = 0; update->srcrect->y = sprite->y; update->srcrect->w = wrap; update->srcrect->h = sprite->image->h; update->dstrect->x =((sprite->x/background->w)+1)*background->w; update->dstrect->y = sprite->y; update->dstrect->w = update->srcrect->w; update->dstrect->h = update->srcrect->h; } } void UpdateScreen(void) { int i; for ( i=0; iy >= (sprite2->y+sprite2->image->h)) || (sprite1->x >= (sprite2->x+sprite2->image->w)) || (sprite2->y >= (sprite1->y+sprite1->image->h)) || (sprite2->x >= (sprite1->x+sprite1->image->w)) ) { return(0); } return(1); } void WaitFrame(void) { static Uint32 next_tick = 0; Uint32 this_tick; /* Wait for the next frame */ this_tick = SDL_GetTicks(); if ( this_tick < next_tick ) { SDL_Delay(next_tick-this_tick); } next_tick = this_tick + (1000/FRAMES_PER_SEC); } /* This of course can be optimized :-) */ void RunGame(void) { int i, j; SDL_Event event; Uint8 *keys; /* Paint the background */ numupdates = 0; for ( i=0; iw; i += background->w ) { SDL_Rect dst; dst.x = i; dst.y = 0; dst.w = background->w; dst.h = background->h; SDL_BlitSurface(background, NULL, screen, &dst); } SDL_UpdateRect(screen, 0, 0, 0, 0); /* Initialize the objects */ player.alive = 1; player.x = (screen->w - player.image->w)/2; player.y = (screen->h - player.image->h) - 1; player.facing = 0; DrawObject(&player); for ( i=0; iw-shots[i].image->w)/2; shots[i].y = player.y - shots[i].image->h; shots[i].alive = 1; Mix_PlayChannel(SHOT_WAV, sounds[SHOT_WAV], 0); } } } reloading = (keys[SDLK_SPACE] == SDL_PRESSED); /* Move the player */ player.facing = 0; if ( keys[SDLK_RIGHT] ) { ++player.facing; } if ( keys[SDLK_LEFT] ) { --player.facing; } player.x += player.facing*PLAYER_SPEED; if ( player.x < 0 ) { player.x = 0; } else if ( player.x >= (screen->w-player.image->w) ) { player.x = (screen->w-player.image->w)-1; } /* Move the aliens */ for ( i=0; ih; aliens[i].facing = 1; } else if ( aliens[i].x >= (screen->w-aliens[i].image->w) ) { aliens[i].x = (screen->w-aliens[i].image->w)-1; aliens[i].y += aliens[i].image->h; aliens[i].facing = -1; } } } /* Move the shots */ for ( i=0; i