#include #include #include #include #include #include #include #include #include #include #include #include #include #define RE(i,n) for(int (i) = 0; (i) < (int) n; ++i) using namespace std; 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; double sqr(double x) { return x*x; } //euclidean distance double dist(double x1, double x2, double y1, double y2) { return sqrt(sqr(x2-x1) + sqr(y2-y1)); } 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, double x, double y, double radius, Uint8 R, Uint8 G, Uint8 B) { if(fabs(radius) < 9000) for(int i = -1-radius + x; i <= x + 1 + radius; ++i) for(int j = y -radius - 1; j <= radius + 1 + y; ++j) if(dist(i,x,j,y) < radius && inBounds(i,j)) { drawPixel(screen, i,j,R,G,B); } } 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 Ball { public: double x; double y; double r; double vx; double vy; int R; int G; int B; double score; double health; double damage; Ball(double xn, double yn, double nr, double nvy) { x = xn; y = yn; r = nr; vy = nvy; R = 255; G = 0; B = 0; vx = 0; damage = 1; health = 1; } void color() { const int ncolors = 6; const int cmapR[ncolors] = {255,0,0,255,255,255}; const int cmapG[ncolors] = {0,0,255,255,0,255}; const int cmapB[ncolors] = {0,255,0,0, 255,255}; R = cmapR[((int) health-1)%ncolors]; G = cmapG[((int) health-1)%ncolors]; B = cmapB[((int) health-1)%ncolors]; } bool check_collision(Ball* b) { if(b == NULL) return false; if(dist(b->x, x, b->y, y) < b->r + r) return true; return false; } void draw(SDL_Surface* screen) { drawCircle(screen, x,y,r,R,G,B); } void step(double time) { x += time*vx; y += time*vy; } }; 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) { if(inBounds(x,y)) drawPixel(screen, x, y, 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(Ball* b) { vector result; if(b == NULL) return result; for(int i = 0; i < 300; ++i) { double tx = (rand()%((int) (2*b->r+1)*100))/100.0-b->r; double ty = (rand()%((int) (2*b->r+1)*100))/100.0-b->r; if(tx*tx + ty * ty > sqr(b->r)) continue; double vr = 200*dist(tx,0,ty,0)/b->r; double ex = tx/dist(tx,0,ty,0); double ey = ty/dist(tx,0,ty,0); tx += b->x; ty += b->y; Particle* temp = new Particle(tx, ty, 1, b->R, b->G, b->B); temp->vx = ex*vr+b->vx; temp->vy = ey*vr+b->vy; temp->life = (rand()%100)/50.0; result.push_back(temp); } return result; } double ground_level = yres - 20; double g = 300.0; string name; //loads the "settings" (color sceme) void loadConstants() { ifstream ins; ins.open("settings.dat"); //not used ins.close(); } //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("Space Canon - v0.02a", NULL); return 0; } double draw_rand() { double result = rand()%10000-5000; result += rand()%10000 - 5000; result /= 7000.0; return result; } class Weapon { public: double next_shot; double reload_time; int Nprojectiles; double projectileRad; int projectile_hp; double projectile_damage; double angle; int x; int y; double Evel; double dvel; double dphi; double transit_vel; Weapon(int nx, int ny) { transit_vel = 2; Evel = 400; dvel = 60; dphi = 0.2; Nprojectiles = 1; projectileRad = 1.9; projectile_hp = 1; projectile_damage = 1; x = nx; y = ny; next_shot = 0; reload_time = 1; angle = M_PI/2; } void step(double t, int right) { if(next_shot > 0) next_shot -= t; angle -= right*t*transit_vel; if(angle < 0.1) angle = 0.1; if(angle > M_PI-0.1) angle = M_PI-0.1; } void draw(SDL_Surface* screen) { //draw cannon; drawCircle(screen, x, y, 10, 255,255,255); double x1 = cos(angle)*15; double y1 = -sin(angle)*15; drawLine(screen, x, y, x + x1, y + y1, 255,255,255); } vector shoot() { vector result; if(next_shot <= 0) { next_shot += reload_time; double sx = x + cos(angle)*15; double sy = y-sin(angle)*15; for(int i = 0; i < Nprojectiles; ++i) { Ball* tempe = new Ball(sx, sy, projectileRad, 0); double angle1 = angle + draw_rand()*dphi; double vel = Evel + draw_rand()*dvel; tempe->vx = cos(angle1) * vel; tempe->vy = -sin(angle1) * vel; tempe->G = 255; tempe->health = projectile_hp; tempe->damage = projectile_damage; result.push_back(tempe); } } return result; } }; class GameHandle { //will always contain the CURRENT highscore double highscore; //level number vector enemies; vector projectiles; vector particles; SDL_Surface* screen; bool game_Quit; bool quickquit; Label* scoreLabel; Label* message; Label* lifeLabel; //-1,0,1 respectively depending on the status of ther arrow keys int down; int right; int space; int lifes; bool game_Over; vector weapons; int current_weapon; void game_reset() { //SEED srand(42+197641283); down = 0; current_weapon = 0; RE(i, weapons.size()) delete weapons[i]; weapons = vector(); Weapon* mg = new Weapon(xres/2, ground_level); mg->reload_time = 0.12; mg->projectile_damage = 1; mg->transit_vel = 4; weapons.push_back(mg); Weapon* shotgun = new Weapon(xres/2, ground_level); shotgun->reload_time = 1; shotgun->projectile_damage = 1; shotgun->Nprojectiles = 12; shotgun->projectileRad = 1.2; shotgun->Evel = 600; shotgun->transit_vel = 4; weapons.push_back(shotgun); Weapon* canon = new Weapon(xres/2, ground_level); canon->reload_time = 1.1; canon->projectile_damage = 3; canon->projectile_hp = 5; canon->projectileRad = 32; canon->transit_vel = 2; canon->Evel = 300; canon->dvel = 20; canon->dphi = 0.07; weapons.push_back(canon); lifes = 5; highscore = 0; game_Quit = false; quickquit = false; // for(int i = 0) } public: ~GameHandle() { delete scoreLabel; // delete message; delete lifeLabel; atexit(SDL_Quit); RE(i, weapons.size()) delete weapons[i]; } GameHandle(SDL_Surface* Nscreen) { highscore = 0; screen = Nscreen; SDL_Rect lpos; lpos.x = 100; lpos.y = 10; lpos.w = 300; lpos.h = 20; lifeLabel = new Label(lltostr(lifes) + " life", lpos); lpos.x = 300; lpos.y = 10; lpos.w = 300; lpos.h = 20; scoreLabel = new Label("Score: " + lltostr(highscore), lpos); } void update_Label() { scoreLabel->setCaption("Score: " + lltostr(highscore)); lifeLabel->setCaption(lltostr(lifes) + " life"); } void paint(bool win, bool showmessage) { blacken(screen); for(int i = 0; i < (int) particles.size(); ++i) particles[i]->draw(screen); for(int i = 0; i < (int) enemies.size(); ++i) enemies[i]->draw(screen); for(int i = 0; i < (int) projectiles.size(); ++i) projectiles[i]->draw(screen); for(int i = 0; i < xres; ++i) drawPixel(screen,i,ground_level,255,255,255); weapons[current_weapon]->draw(screen); SDL_Rect r; r.x = 0; r.y = ground_level+1; r.w = xres; r.h = yres - ground_level; SDL_FillRect(screen, &r, 0); scoreLabel->draw(screen); lifeLabel->draw(screen); if(showmessage) message->draw(screen); SDL_Flip(screen); } void step(int steps, bool inGame, double t) { if(inGame) { // RE(i,weapons.size()) weapons[current_weapon]->step(1/60.0,right); if(space) { vector nproj = weapons[current_weapon]->shoot(); RE(i, nproj.size()) projectiles.push_back(nproj[i]); } //spawning a new Enemy every few steps int lvl = steps/3000 + 1; if(!(steps % 3000)) cout << "reached level " << lvl << "! " << endl; double progression = steps % 3000; if(rand()%70000 < progression/2 + 500) { double rad = max(rand()%300/(5.0+lvl),50.0/(3+lvl)+2); double vel = 10*lvl + rand()%50; Ball* tempe = new Ball(rand()%xres,0-rad, rad, vel); for(int i = 1; i < lvl; ++i) if(rand()%2) tempe->health++; tempe->score = tempe->health*(2000.0/tempe->r + tempe->vy); tempe->color(); enemies.push_back(tempe); } vector nenemies; for(int i = 0; i < (int) enemies.size(); ++i) { enemies[i]->step(1/60.0); //checking wether an enemy was killed bool shot = false; for(int j = 0; j < (int) projectiles.size(); ++j) if(!shot) if(enemies[i]->check_collision(projectiles[j])) { enemies[i]->health -= projectiles[j]->damage; projectiles[j]->health -= enemies[i]->damage; if(enemies[i]->health <= 0) { shot = true; highscore += enemies[i]->score; vector tempP = makeDeathAnimation(enemies[i]); for(int k = 0; k < (int) tempP.size(); ++k) particles.push_back(tempP[k]); delete enemies[i]; } if(projectiles[j]->health <= 0) { vector tempP = makeDeathAnimation(projectiles[j]); for(int k = 0; k < (int) tempP.size(); ++k) particles.push_back(tempP[k]); projectiles[j]->x = -10000; } } if(shot) continue; //checking wether an enemy has reached the bottom line if(enemies[i]->y + enemies[i]->r > ground_level) { lifes -= 1; vector tempP = makeDeathAnimation(enemies[i]); for(int j = 0; j < (int) tempP.size(); ++j) particles.push_back(tempP[j]); delete enemies[i]; continue; } nenemies.push_back(enemies[i]); } enemies = nenemies; vector nprojectiles; for(int i = 0; i < (int) projectiles.size(); ++i) { if(projectiles[i]->x == -10000) { delete projectiles[i]; continue; } projectiles[i]->step(1/60.0); //checking wether a projectile has reached the edge if(!inBounds(projectiles[i]->x, projectiles[i]->y)) { vector tempP = makeDeathAnimation(projectiles[i]); for(int j = 0; j < (int) tempP.size(); ++j) particles.push_back(tempP[j]); delete projectiles[i]; continue; } nprojectiles.push_back(projectiles[i]); } projectiles = nprojectiles; } //deleting old particles vector nparticles; for(int i = 0; i < (int) particles.size(); ++i) { if(particles[i]->step(1/60.0) && inBounds(particles[i]->x,particles[i]->y)) { nparticles.push_back(particles[i]); } else { delete particles[i]; } } particles = nparticles; if(lifes <= 0) game_Over = true; } 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); int ndown = -keyState[SDLK_UP]+keyState[SDLK_DOWN]; if(ndown != down) { if(down == -1) { int n = (current_weapon + 1)%weapons.size(); weapons[n]->angle = weapons[current_weapon]->angle; current_weapon = n; } if(down == 1) { int n = (current_weapon - 1 + weapons.size())%weapons.size(); weapons[n]->angle = weapons[current_weapon]->angle; current_weapon = n; } } down = ndown; right = -keyState[SDLK_LEFT]+keyState[SDLK_RIGHT]; space = keyState[SDLK_SPACE]; } // 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() { //won or lost the game game_Over = false; //resets the game variables to startpoint game_reset(); 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); if(game_Over) break; update_Label(); steps++; time += 1/60.0; paint(true,false); Uint32 time = SDL_GetTicks()-start; if(1000/60.0 - time > 0) SDL_Delay(1000/60.0 - time); } cout << "You died! Your highscore was " << highscore << endl; } }; //a console io menu void pregame_menu(string& name) { cout << "This is a simple space game" << endl; cout << "Please enter your name:"; cin >> name; // cout << "Please enter difficulty (0-100):"; // cin >> difficulty; } int main() { loadConstants(); pregame_menu(name); if(init_libs(&screen)) return 1; GameHandle game = GameHandle(screen); game.play(); return 0; }