#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "random.h" using namespace std; //loads a bitmap from file and converts it to screen properties SDL_Surface* loadBMP(std::string filename) { SDL_Surface* loadedImage = NULL; SDL_Surface* convertedImage = NULL; loadedImage = IMG_Load(filename.c_str()); if(loadedImage != NULL) { convertedImage = SDL_DisplayFormat(loadedImage); if(convertedImage) { Uint32 colorkey = SDL_MapRGB(convertedImage->format, 10, 10, 10); SDL_SetColorKey(convertedImage, SDL_SRCCOLORKEY, colorkey); SDL_FreeSurface(loadedImage); return convertedImage; } cout << "Error: Could not convert Image" << filename << '.' << endl; } cout << "Error: Could not load bitmap from file " << filename << endl; return NULL; } string lltostr(const long long& l) { stringstream str; str << l; return str.str(); } class Label { protected: string caption; SDL_Surface* image; // int textSize; SDL_Color textColor; int render(); public: SDL_Rect pos; Label(); Label(string ncaption, SDL_Rect& npos); ~Label(); void draw(SDL_Surface* screen); int setTextSize(int ntextSize); void setTextColor(SDL_Color textColor); int setCaption(string ncaption); }; Label::Label() { pos.x = 0; pos.y = 0; pos.w = 0; pos.h = 14; caption = ""; textColor.r = 255; textColor.g = 255; textColor.b = 255; render(); } Label::Label(string ncaption, SDL_Rect& npos) { pos = npos; textColor.r = 255; textColor.g = 255; textColor.b = 255; caption = ncaption; npos.w = render(); } int Label::setTextSize(int ntextSize) { pos.h = ntextSize; SDL_FreeSurface(image); return render(); } void Label::setTextColor(SDL_Color ntextColor) { textColor = ntextColor; SDL_FreeSurface(image); render(); } int Label::setCaption(string ncaption) { if(caption == ncaption) return pos.w; caption = ncaption; SDL_FreeSurface(image); return render(); } int Label::render() { TTF_Font *font = TTF_OpenFont("fonts/OpenSans-Semibold.ttf",pos.h); image = NULL; image = TTF_RenderText_Solid(font, caption.c_str(),textColor); if(caption.size() == 0) cout << "empty caption!!!" << endl; if(image == NULL) { cout << "Error rendering Label with caption " << caption << endl; return 0; } TTF_CloseFont(font); pos.w = image->w; return image->w; } void Label::draw(SDL_Surface* screen) { SDL_Rect temp = pos; SDL_BlitSurface(image, NULL, screen, &temp); } Label::~Label() { SDL_FreeSurface(image); } int xres = 600; int yres = 450; SDL_Surface *screen; bool inBounds(int x, int y) { if(x >= 0 && x < xres && y >= 0 && y < yres) return true; return false; } void drawPixel(SDL_Surface *screen,int x, int y, Uint8 R, Uint8 G, Uint8 B) { Uint32 color = SDL_MapRGB(screen->format, R, G, B); SDL_Rect r; r.x = x; r.y = y; r.w = 1; r.h = 1; SDL_FillRect(screen,&r,color); } void drawLine(SDL_Surface* screen,double x0,double y0,double x1, double y1, Uint8 R, Uint8 G, Uint8 B) { double x = x0; double y = y0; double dir_x = x1 - x0; double dir_y = y1 - y0; double norm = dir_x*dir_x + dir_y*dir_y; dir_x /= sqrt(2*norm); dir_y /= sqrt(2*norm); while((x1-x)*dir_x + (y1-y)*dir_y > 0) { drawPixel(screen, x, y, R,G,B); x += dir_x; y += dir_y; } } void drawCircle(SDL_Surface* screen, SDL_Surface* preBall, double x, double y, double angle, double radius, Uint8 R, Uint8 G, Uint8 B) { int tempseed = rand(); srand(87); Uint32 colorkey = SDL_MapRGB(preBall->format, 10, 10, 10); SDL_FillRect(preBall,NULL,colorkey); for(int i = 0; i <= 2*radius; ++i) for(int j = 0; j <= 2*radius; ++j) if(((i-radius+0.5)*(i-radius+0.5)+(j-radius+0.5)*(j-radius+0.5) <= radius*radius)) { int texturkonstante = 60; int alt1 = rand()%texturkonstante-texturkonstante/2; int alt2 = rand()%texturkonstante-texturkonstante/2; int alt3 = rand()%texturkonstante-texturkonstante/2; drawPixel(preBall, i,j,R+alt1,G+alt2,B+alt3); } SDL_Rect drawpos; SDL_Surface* temp = preBall; temp = rotozoomSurface(temp,angle/M_PI*180,1,0); drawpos.x = x-temp->w/2; drawpos.y = y - temp->h/2; SDL_BlitSurface(temp,NULL,screen,&drawpos); SDL_FreeSurface(temp); srand(tempseed); } void blacken(SDL_Surface* screen) { SDL_FillRect(screen,NULL,0); } Uint8 playerR = 100; Uint8 playerG = 100; Uint8 playerB = 100; Uint8 obstacleR = 255; Uint8 obstacleG = 100; Uint8 obstacleB = 100; class Obstacle { public: SDL_Rect r; int vx; Obstacle(double xn, double yn, double width, double height) { r.x = xn; r.y = yn; r.h = height; r.w = width; vx = 0; } bool check_death(double x_pos, double y_pos, double radius) { if(x_pos + radius < r.x || x_pos - radius > r.x + r.w || y_pos + radius < r.y || y_pos - radius > r.y + r.h) return false; return true; } void draw(SDL_Surface* screen,double levelx, double levely) { SDL_Rect temp = r; temp.x -= levelx; temp.y -= levely; temp.y = yres - temp.y- temp.h; SDL_FillRect(screen, &temp, SDL_MapRGB(screen->format, obstacleR, obstacleG, obstacleB)); } void step(double time) { r.x += time*vx; } }; class Particle { public: double x; double y; double vx; double vy; double life; // 0 = static, 1 = strife, 2 = randomwalk, 3 = xrandomwalk int mode; Uint8 R,G,B; Particle() { x = rand()%xres; y = rand()%yres; life = rand()%20; R = rand()%256; G = rand()%256; B = rand()%256; } Particle(double xn, double yn, int nmode, Uint8 Rn, Uint8 Gn, Uint8 Bn) { mode = nmode; x = xn; y = yn; R = Rn; G = Gn; B = Bn; life = rand()%20; } void draw(SDL_Surface* screen,double levelx,double levely) { if(inBounds(x-levelx,y-levely)) drawPixel(screen, x-levelx, yres- (y-levely), R, G, B); } bool step(double time) { life -= time; // 0 = static, 1 = strife, 2 = randomwalk, 3 = xrandomwalk if(mode == 1){ x += vx*time; y += vy*time; } else if(mode == 2) { x += (rand()%3-1)/2.0; y += (rand()%3-1)/2.0; } else if(mode == 3) { x += (rand()%3-1)/8.0; y += vy*time; } return (life >= 0); } }; vector makeDeathAnimation(double x, double y, double r, double vxb, double vyb) { vector result; for(int i = 0; i < 2000; ++i) { double tx = rand()%((int) (2*r+1))-r; double ty = rand()%((int) (2*r+1))-r; if(tx*tx + ty * ty > r*r) continue; tx += x; ty += y; Particle temp = Particle(tx, ty, 1, playerR, playerG, playerB); double vb = (rand()%5000)/30.0; double vp = ((rand()%10000)/10000.0)*2*M_PI; temp.vx = cos(vp)*vb+vxb*(rand()%1000)/1000.0; temp.vy = sin(vp)*vb+vyb*(rand()%1000)/1000.0; temp.life = (rand()%100)/50.0; result.push_back(temp); } return result; } double ground_level = 20; double ball_rad = 8; double y_pos = ground_level + ball_rad; double g = 300.0; double c_r = 0.004; double win_bonus = 5000; string name; //loads the "settings" (color sceme) void loadConstants() { ifstream ins; ins.open("settings.dat"); int i = 0; ins >> i; playerR = i; ins >> i; playerG = i; ins >> i; playerB = i; ins >> i; obstacleR = i; ins >> i; obstacleG = i; ins >> i; obstacleB = i; ins.close(); } //a console io menu with limited possibility to set difficulty and hardcore mode void pregame_menu(string& name, int& difficulty, double& position_multiplier, bool& hardcore) { cout << "This is a simple jump and run" << endl; cout << "Please enter your name:"; cin >> name; cout << "Please enter difficulty (0-100):"; cin >> difficulty; cout << "Do you want to play in hardcore mode(0/1)?"; cin >> hardcore; if(hardcore) { cout << "You are insane. +100% score!" << endl; } else cout << "Very sane decision..." << endl; if(difficulty >= 0) cout << "solvability not guarenteed! Have fun!" << endl; if(difficulty < 0) cout << "This should be a piece of cake and you should feel bad!" << endl; difficulty = max(-50,difficulty); if(difficulty > 100) difficulty = 100; cout << "difficulty = " << difficulty << endl; } //initializes the imported libs and opens a window int init_libs(SDL_Surface **screen) { if(SDL_Init(SDL_INIT_VIDEO) == -1) { cout << "Error: Could not initialize SDL" << endl; return 1; } *screen = SDL_SetVideoMode(xres,yres,32,SDL_SWSURFACE); if(!screen) { cout << "could not initialize screen" << endl; return 1; } if(TTF_Init() == -1) { cout << "could not initialize True Fonts" << endl; return 1; } SDL_WM_SetCaption("Ballroller - v0.3", NULL); return 0; } class GameHandle { //will always contain the CURRENT highscore double highscore; //level number int difficulty; bool hardcore; double x_pos; double y_pos; double x_vel; double y_vel; double vphi; double phi; double position_multiplier; vector obstacles; vector particles; SDL_Surface* preBall; SDL_Surface* screen; bool game_Quit; bool quickquit; Label* scoreLabel; Label* message; //-1,0,1 respectively depending on the status of ther arrow keys int down; int right; double g_x; double g_y; void game_reset() { //SEED srandn(2334508 + difficulty); srand(2334508 + difficulty); highscore = 1000; x_pos = 0; y_pos = 0; y_vel = 0; x_vel = 0; vphi = 0; phi = 0; position_multiplier = 3; //higher levels give more score position_multiplier += difficulty/40.0; //hardcore gives 100% score bonus if(hardcore) highscore *= 2; game_Quit = false; quickquit = false; //creating the level obstacles = vector(); obstacles.push_back(Obstacle(-50,ground_level,10,200)); //Longest Jump: 608 Highest Jump: 150 (TODO: filter obstacles based on that) int opos = 200; while(opos < 10000) { int blub = 4+randn()%(162+difficulty/2-4); obstacles.push_back(Obstacle(opos,ground_level,blub,(176 + difficulty/2)/2-blub/2)); opos += randn()%(890-9*difficulty/2); } //in hardcore mode, there are boxes going from right to left if(hardcore) for(int i = 1; i < 110; ++i) { Obstacle tempo = Obstacle(i*300,ground_level, 10, 10); tempo.vx = -60; obstacles.push_back(tempo); } } public: ~GameHandle() { SDL_FreeSurface(preBall); atexit(SDL_Quit); delete scoreLabel; } GameHandle(int Ndifficulty, bool Nhardcore, SDL_Surface* Nscreen) { difficulty = Ndifficulty; hardcore = Nhardcore; highscore = 1000; screen = Nscreen; preBall = loadBMP("blank.bmp"); for(int i = 0; i < 10*60*5; ++i) { Particle p = Particle(rand()%11000 - 300,ground_level + rand()%((int)(yres-ground_level)),3,255,255,255); p.life = 1000; p.vy = -5-rand()%30/9.0; particles.push_back(p); } SDL_Rect lpos; lpos.x = 300; lpos.y = 10; lpos.w = 300; lpos.h = 20; scoreLabel = new Label("Score: " + lltostr(highscore), lpos); } void update_scoreLabel() { scoreLabel->setCaption("Score: " + lltostr(highscore)); } void paint(bool win, bool showmessage) { blacken(screen); for(int i = 0; i < (int) particles.size(); ++i) particles[i].draw(screen,x_pos-xres/4,0); for(int i = 0; i < (int) obstacles.size(); ++i) obstacles[i].draw(screen,x_pos-xres/4,0); if(win) drawCircle(screen, preBall, xres/4, fabs(yres-y_pos), phi, ball_rad, playerR, playerG, playerB); for(int i = 0; i < xres; ++i) drawPixel(screen,i,yres-ground_level,255,255,255); scoreLabel->draw(screen); if(showmessage) message->draw(screen); if(hardcore) drawLine(screen,500,20,500 + g_x/3,20 - g_y/3,255,255,255); SDL_Flip(screen); } void step(int steps, bool inGame, double t) { if(inGame) { //spawning a new obstacle for hardcoremode if(!(steps%300) && hardcore) { Obstacle tempo = Obstacle(110*300,ground_level, 10, 10); tempo.vx = -60; obstacles.push_back(tempo); } for(int i = 0; i < (int) obstacles.size(); ++i) obstacles[i].step(1/60.0); x_pos = x_pos + x_vel/60.0; y_pos = y_pos + y_vel/60.0; //updating highscore highscore -= 1.0; highscore += x_vel/60.0*position_multiplier*(1+hardcore); phi = phi + vphi/60.0; if(hardcore) { double vart = (sin(t))/7; g_x = g*sin(vart); g_y = - g*cos(vart); if(y_pos > ground_level+ball_rad) { y_vel = y_vel*(1.0-c_r) + g_y/60; x_vel = x_vel*(1.0-c_r) + right*80/60.0 + g_x/60; } if(y_pos <= ground_level+ball_rad) { x_vel = x_vel*(1.0-c_r) + right*140./60.0 + g_x/60; y_pos = ground_level+ball_rad; y_vel = fabs(y_vel); if(down == -1) y_vel += 900; y_vel *= 0.25; /*double Dvphi = (x_vel/ball_rad+vphi)/2; x_vel -= Dvphi*2;*/ vphi += x_vel/ball_rad; } } else { if(y_pos > ground_level+ball_rad) { y_vel = y_vel*(1.0-c_r) - g/60.0; x_vel = x_vel*(1.0-c_r) + right*80/60.0; } if(y_pos <= ground_level+ball_rad) { x_vel = x_vel*(1.0-c_r) + right*140./60.0; y_pos = ground_level+ball_rad; y_vel = fabs(y_vel); if(down == -1) y_vel += 900; y_vel *= 0.25; vphi = -x_vel/ball_rad; } } } //deleting old particles vector nparticles; for(int i = 0; i < (int) particles.size(); ++i) { if(particles[i].step(1/60.0) && particles[i].y > ground_level) { nparticles.push_back(particles[i]); } } particles = nparticles; for(int i = 0; i < 1; ++i) { Particle p = Particle(-300 + rand()%11001,yres,3,255,255,255); p.vy = -5-rand()%30/9.0; p.life = 1000; particles.push_back(p); } } void handle_Events(bool &game_Quit, int &down, int& right) { SDL_Event event; while(SDL_PollEvent(&event)) { if(event.type == SDL_QUIT) { game_Quit = true; break; } } Uint8 *keyState = SDL_GetKeyState(NULL); down = -keyState[SDLK_UP]+keyState[SDLK_DOWN]; right = -keyState[SDLK_LEFT]+keyState[SDLK_RIGHT]; } void animate_death() { vector deathanimation = makeDeathAnimation(x_pos,y_pos,ball_rad,x_vel, y_vel); for(int i = 0; i < (int) deathanimation.size(); ++i) particles.push_back(deathanimation[i]); } void play() { //closes gamewindow bool quit_Game = false; while(!quit_Game) { //won or lost the game bool game_Over = false; //resets the game variables to startpoint game_reset(); bool win = false; double time = 0; int steps = 0; while(!game_Over) { Uint32 start = SDL_GetTicks(); handle_Events(game_Quit,down, right); if(game_Quit) { game_Over = true; break; } step(steps, true,time); for(int i = 0; i < (int) obstacles.size(); ++i) { if(obstacles[i].check_death(x_pos,y_pos,ball_rad)) { cout << "Collision: You lost the game" << endl; cout << "Better luck next time!" << endl; update_scoreLabel(); cout << "Your highscore: " << highscore << endl; game_Over = true; animate_death(); } } if(game_Over) break; update_scoreLabel(); steps++; time += 1/60.0; if(highscore <= 0) { cout << "TIMEOUT: You lost the game." << endl; cout << "Better luck next time!" << endl; game_Over = true; animate_death(); break; } if(x_pos >= 10000) { cout << "You win the game!" << endl; cout << "Your highscore: " << highscore << endl; update_scoreLabel(); game_Over = true; win = true; break; } paint(true,false); Uint32 time = SDL_GetTicks()-start; if(1000/60.0 - time > 0) SDL_Delay(1000/60.0 - time); } /*finished a game, end-Game screen*/ ifstream ins; ins.open("highscore.dat"); vector > highlist; for(int i = 0; i < 10; ++i) { pair temp; ins >> temp.first; if(!ins.good()) break; ins >> temp.second; if(ins.good()) { highlist.push_back(temp); } else break; } ins.close(); sort(highlist.begin(), highlist.end()); if(highlist.size() < 10 || highlist[0].first < highscore) { cout << "Congratulations! You made a new highscore." << endl; pair youscore; youscore.first = highscore; youscore.second = name; highlist.push_back(youscore); sort(highlist.begin(), highlist.end()); ofstream ofs; ofs.open("highscore.dat"); if(highlist.size() == 11) { for(int i = 0; i < 10; ++i) { ofs << highlist[i+1].first << " " << highlist[i+1].second << endl; } } else { for(int i = 0; i < (int)highlist.size(); ++i) { ofs << highlist[i].first << " " << highlist[i].second << endl; } } ofs.close(); } cout << "\n**HIGHSCORE**\n"; for(int i = min(10,(int) highlist.size()-1); i > 0; --i) { cout << highlist[i].second << ": " << highlist[i].first << '\n'; } if(highlist.size() < 11) cout << highlist[0].second << ": " << highlist[0].first << '\n'; cout << endl; SDL_Rect mpos; mpos.x = 25; mpos.y = 180; mpos.w = 300; mpos.h = 32; if(win) message = new Label("YOU WIN! - Press space to restart",mpos); else message = new Label("GAME OVER - Press space to restart", mpos); while(quit_Game == false) { SDL_Event event; SDL_PollEvent(&event); Uint8 *keyState = SDL_GetKeyState(NULL); if(event.type == SDL_QUIT || keyState[SDLK_ESCAPE]) { quit_Game = true; break; } if(keyState[SDLK_SPACE] || keyState[SDLK_RETURN]) break; step(steps,false,time); paint(win,true); SDL_Delay(1000/60.0); } delete message; } } }; int main() { loadConstants(); bool hardcore = false; int difficulty = 0; double position_multiplier = 1; pregame_menu(name,difficulty, position_multiplier, hardcore); if(init_libs(&screen)) return 1; GameHandle game = GameHandle(difficulty, hardcore,screen); game.play(); return 0; }