/* flyhard.cpp - the main loop Copyright (C) 2006-2007 Mark boyd 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 fun to play, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include #include #include #include #include #include #include #include "movers.h" #include "etc.h" #include "arena.h" #include "game.h" #include "enemies.h" #include "text.h" #include "levels.h" #include "loadimag.h" #include "shop.h" #include "sound.h" #include "dirs.h" #include "debug.h" //"tickometer" is frame-fate info in top-left of screen //Useful for development. Real users won't like it much. const bool tickometer=false; bool do_title_screen(SDL_Surface *); void play_game(SDL_Surface *); void update_player_status(SDL_Surface *screen); void show_description(SDL_Surface *screen, const std::string &description); void do_printscreen(SDL_Surface *screen); namespace hi_scores { void draw(SDL_Surface *screen); void new_score(SDL_Surface *screen, int s); } namespace flags { bool fullscreen=true; } //Command-line arguments //Return true to prevent running bool process_args(int argc, char *argv[]) { if (std::find(argv, argv+argc, std::string("--help"))!=argv+argc) { std::cout<<"It's a game, you know how to play games, don't you?\n" "\n" "Optional arguments:\n" "--show-dirs: show directories where flyhard looks for stuff\n" "--fullscreen: run in fullscreen mode\n" "--window: run in window\n" "--help: show this text which you are reading right now\n" <format, 255,255,255,255); SDL_Event event; while (SDL_PollEvent(&event)); int start=0; int line=0; for(int i=0; i=0) { text::draw(std::string(description, start, i-start), screen, left, top+line*gap, white); } start=i+1; line++; SDL_Delay(50); } } text::draw("Press any key", screen, left, 420, white); while (!(SDL_PollEvent(&event) &&event.type==SDL_KEYDOWN)); game &g=*(game::the_game); g.m_key_flags.up=false; g.m_key_flags.left=false; g.m_key_flags.right=false; g.m_key_flags.tractor=false; g.m_key_flags.fire=false; } bool do_title_screen(SDL_Surface *screen) { SDL_Surface *ts=load_image("title.png"); SDL_BlitSurface(ts, NULL, screen, NULL); SDL_UpdateRect(screen, 0, 0, 0, 0); const Uint32 white=SDL_MapRGBA(screen->format, 255,255,255,255); text::draw("Press (almost) any key to start", screen, 300, 300, white); text::draw("Or escape to quit", screen, 300, 315, white); SDL_Event event; while (!(SDL_PollEvent(&event) && event.type==SDL_KEYDOWN)); return (event.key.keysym.sym==SDLK_ESCAPE); } void play_game(SDL_Surface *screen) { Uint32 black=SDL_MapRGB(screen->format, 0,0,0); SDL_FillRect(screen, 0, black); SDL_UpdateRect(screen, 0,0,0,0); const int screen_w=640; const int screen_h=480-TILE_SIZE; game g; game::the_game=&g; arena the_arena; g.m_arena = &the_arena; g.m_player_status.lives=3; g.m_player_status.score=0; g.m_player_status.money=0; g.m_player_status.shield=MAX_SHIELD; g.m_player_status.in_shop=false; g.m_player_status.max_energy=calc_ship_battery(); g.m_player_status.energy=calc_ship_battery(); g.m_player_status.invulnerability=0; // //Notice that the x and y values of these need to match the ship graphic //(otherwise we'll have weapon mounts floating in space - not good) // weapon_mount mounts[]= { //Lots and lots of weapon mounts! //The intention here is to make the Xenon 2 ship look poorly armed :) // x y angle name 0 starting weapon { 0, -15, 0.0, "Front Centre", 0, WT_PEASHOOTER}, { -2.5, -14.8, -0.1, "Front Left", 0, WT_NOTHING}, { 2.5, -14.8, 0.1, "Front Right", 0, WT_NOTHING}, { -15, 13.5, 0, "Left Wing", 0, WT_NOTHING}, { 15, 13.5, 0, "Right Wing", 0, WT_NOTHING}, { -9, 0, -pi/2, "Left", 0, WT_NOTHING}, { 9, 0, pi/2, "Right", 0, WT_NOTHING}, { -9, 4, -pi/2, "Left", 0, WT_NOTHING}, { 9, 4, pi/2, "Right", 0, WT_NOTHING}, { -2, 16, pi, "Rear left", 0, WT_NOTHING}, { 2, 16, pi, "Rear right", 0, WT_NOTHING}, }; for(int i=0; i(system_slots, ST_EMPTY); player_status old_player_status={-1}; //different to current status int current_level=0; const int frame=40; bool dont_stop=true; int old_time=0; int frame_gap=0; level_loader levels(dirs::level_file); while(dont_stop) { //Start next level { std::string description; bool loaded=false; while (!loaded && !levels.end_of_file()) { if (!levels.next_level(the_arena, description)) { //Bad level - non-fatal error DBG_LOG(std::string("Bad level in file ")+dirs::level_file+"; skipping this level"); } else { loaded=true; } } if (!loaded) dont_stop=false; if (description.size()>0) { show_description(screen, description); } } //Main game loop while(dont_stop && !the_arena.level_complete() && !the_arena.player_is_dead()) { if (g.m_player_status.in_shop) { shop(screen); } int time=SDL_GetTicks(); //Get input SDL_Event event; while (SDL_PollEvent(&event)) { if (event.type==SDL_KEYDOWN) { if (event.key.keysym.sym==SDLK_UP) g.m_key_flags.up=true; if (event.key.keysym.sym==SDLK_LEFT) g.m_key_flags.left=true; if (event.key.keysym.sym==SDLK_RIGHT) g.m_key_flags.right=true; if (event.key.keysym.sym==SDLK_z) g.m_key_flags.tractor=true; if (event.key.keysym.sym==SDLK_SPACE) g.m_key_flags.fire=true; if (event.key.keysym.sym==SDLK_ESCAPE) dont_stop=false; if (event.key.keysym.sym==SDLK_PRINT) do_printscreen(screen);; } if (event.type==SDL_KEYUP) { if (event.key.keysym.sym==SDLK_UP) g.m_key_flags.up=false; if (event.key.keysym.sym==SDLK_LEFT) g.m_key_flags.left=false; if (event.key.keysym.sym==SDLK_RIGHT) g.m_key_flags.right=false; if (event.key.keysym.sym==SDLK_z) g.m_key_flags.tractor=false; if (event.key.keysym.sym==SDLK_SPACE) g.m_key_flags.fire=false; } } //Perform magic { float seconds = 1.0*frame/1000; //frame is a number of milliseconds the_arena.do_stuff(seconds); the_arena.apply_forces(seconds); the_arena.move_stuff_and_do_collisions(seconds); } //Blit arena to screen int screen_x=std::max(0, std::min(TILE_SIZE*the_arena.width()-screen_w, int(the_arena.get_player_x()-screen_w/2))); int screen_y=std::max(0, std::min(TILE_SIZE*the_arena.height()-screen_h, int(the_arena.get_player_y()-screen_h/2))); SDL_Rect bs={screen_x, screen_y, screen_w, screen_h}; SDL_BlitSurface(the_arena.bitmap(), &bs, screen, 0); //blit movers to screen std::vector new_rects; for(int i=0; iw,m.pic()->h}; to.x-=screen_x; to.y-=screen_y; SDL_Rect from={0, 0, to.w, to.h}; int height = std::min(from.h, screen_h-to.y); int width = std::min(from.w, screen_w-to.x); if (width >0 && height > 0 ) { from.w=width; from.h=height; //to.w and to.h are ignored, so don't bother updating them :p SDL_BlitSurface(the_arena.movers()[i]->pic(), &from, screen, &to); } } //Invulnerability doesn't last forever if (g.m_player_status.invulnerability>0) { g.m_player_status.invulnerability=std::max(0, g.m_player_status.invulnerability-1.0*frame/1000); } if (!(g.m_player_status == old_player_status)) { update_player_status(screen); old_player_status=g.m_player_status; } if (tickometer) { //Ticks: 1000/(frame rate) text::draw(int2str(frame_gap), screen, 10, 10, ~0); } SDL_UpdateRect(screen,0,0,0,0); SDL_Delay(std::max(0, time+frame-SDL_GetTicks())); int current_time = SDL_GetTicks(); frame_gap= current_time-old_time; old_time= current_time; } if (the_arena.level_complete()) { SDL_Rect bs={0, 0, screen_w, screen_h}; fade_out(screen, bs); //Throw the player a bone :) g.m_player_status.shield=MAX_SHIELD; current_level++; } if (the_arena.player_is_dead()) { dont_stop = false; } } hi_scores::new_score(screen, g.m_player_status.score); } void do_printscreen(SDL_Surface *screen) { static int n=0; SDL_SaveBMP(screen, ("FlyHardScreenShot"+int2str(n++)+".bmp").c_str()); } namespace hi_scores { const int num=10; const int start_scores[num]={500000,300000,200000,100000, 50000, 20000, 10000, 5000, 2000, 1000}; const char *start_people[num]={"Mark","Mark","Mark","Mark","Mark","Mark","Mark","Mark","Mark","Mark"}; const int title_top=20; const int top=50; const int gap=20; const int people_left=100; const int score_left=400; std::vector &scores() { static bool first=true; static std::vector sco; if (first) { for(int i=0; i &people() { static bool first=true; static std::vector peo; if (first) { for(int i=0; iformat, 255,255,255); std::string title("Hardest Fliers"); text::draw(title, screen, (screen->w-text::width(title))/2, title_top, white); for(int i=0; iformat, 0,0,0); Uint32 white=SDL_MapRGB(screen->format, 255,255,255); if (s<=scores()[num-1]) { //player didn't get a high score //TODO: rub his nose in it? } else { //find where to insert the new score int pos; for(pos=0; posw-text::width(blurb))/2, title_top+gap, white); std::string &new_name=people()[pos]; SDL_Event event; SDL_EnableUNICODE(1); do { while (!(SDL_PollEvent(&event) && event.type==SDL_KEYDOWN)); int k=event.key.keysym.unicode&0xff; if ((k==SDLK_BACKSPACE || k==SDLK_DELETE) && new_name.size()) { SDL_Rect r={people_left, top+gap*pos, text::width(new_name), gap}; SDL_FillRect(screen, &r, black); SDL_UpdateRect(screen, r.x,r.y,r.w,r.h); new_name=std::string(new_name.begin(), new_name.end()-1); sound::play(sound::CLANG); } else if (k>=32 && k<128) { new_name+=char(k); sound::play(sound::PFFT); } text::draw(new_name, screen, people_left, top+gap*pos, white); }while(event.key.keysym.sym != SDLK_RETURN); //What? you couldn't even bother to write your name? if (new_name=="") new_name="Somebody very lazy"; sound::play(sound::BOOM); SDL_EnableUNICODE(0); //todo: do_laterify } animate(screen); } struct firework { float x; float y; float dx; float dy; }; struct fader { float x; float y; float dx; float dy; int r,g,b; float life; }; void hi_scores::animate(SDL_Surface *screen) { Uint32 black=SDL_MapRGB(screen->format, 0,0,0); const float fw_speed = 400.0; const float framesps = 25; const float grav = 200; const float det_speed = 50; const int fader_num=200; const int fader_speed = 100; const float fader_time = 0.8; SDL_Surface *fw_pic = create_surface(2,2); Uint32 white=SDL_MapRGB(fw_pic->format, 255,255,255); SDL_FillRect(fw_pic, 0, white); SDL_UpdateRect(fw_pic,0,0,0,0); std::list fireworks; std::list faders; firework f={0, screen->h, fw_speed*0.1, -fw_speed}; fireworks.push_back(f); SDL_Event event; while (!(SDL_PollEvent(&event) && event.type==SDL_KEYDOWN)) { //create fws if (randu() < 3.0/framesps) { float theta = (randu()*2-1)*0.1; firework f={randint(screen->w), screen->h, fw_speed*sin(theta), -fw_speed*cos(theta)*(0.8+0.2*randu())}; fireworks.push_back(f); sound::play(sound::PFFT, 0.3); } //inc_stuff for(std::list::iterator i=fireworks.begin(); i!=fireworks.end();) { i->x += i->dx/framesps; i->y += i->dy/framesps; i->dy += grav/framesps; if (i->dy >= det_speed) { for(int f=0; fx, i->y, sqrt(1.0-dz*dz)*sin(theta)*fader_speed, sqrt(1.0-dz*dz)*cos(theta)*fader_speed, 128+randint(128),128+randint(128),128+randint(128), fader_time}; faders.push_back(fa); } sound::play(sound::POP, 0.5); fireworks.erase(i++); } else { ++i; } } for(std::list::iterator i=faders.begin(); i!=faders.end();) { i->x += i->dx/framesps; i->y += i->dy/framesps; i->life -= 1.0/framesps; if (i->life<0) { faders.erase(i++); } else { ++i; } } //draw stuff SDL_FillRect(screen, 0, black); for(std::list::iterator i=fireworks.begin(); i!=fireworks.end(); ++i) { SDL_Rect from={0, 0, fw_pic->w, fw_pic->h}; SDL_Rect to={int(i->x), int(i->y), fw_pic->w, fw_pic->h}; SDL_BlitSurface(fw_pic, &from, screen, &to); } for(std::list::iterator i=faders.begin(); i!=faders.end(); ++i) { float ff = minmax(0.0, i->life/fader_time, 1.0); putpixel(screen, int(i->x), int(i->y), SDL_MapRGBA(screen->format, int(ff*i->r),int(ff*i->g),int(ff*i->b),SDL_ALPHA_OPAQUE)); } hi_scores::draw(screen); SDL_UpdateRect(screen,0,0,0,0); SDL_Delay(int(1000.0/framesps)); } } #ifdef USE_EVIL_WINMAIN_HACK #include "evil_winmain_hack.h" #endif