void WorldStage::SaveMapToFile(RegionalMap *map) { std::stringstream ss; const int MAP_WIDTH=80, MAP_HEIGHT=80; //Open a file based on map position ss << "region." << map->startX/MAP_WIDTH << "." << map->startY/MAP_HEIGHT; ALLEGRO_FILE *file = al_fopen(ss.str().c_str(), "w"); if(file) { //Write magic number to it and newline al_fputs(file, "b272bda9bf0c1cdcba614b5ed99c4d62"); al_fputs(file, "\n"); //Write version number and newline al_fputs(file, "0\n"); //Write each tile as a 32le for(int y = 0; y < MAP_HEIGHT; y++) for(int x = 0; x < MAP_WIDTH; x++) { al_fwrite32le(file, map->tile[x][y].materialTypeIndex); } //Write each actor id as a 32le for(auto actorId = map->actorIDs.begin(); actorId != map->actorIDs.end(); actorId++) { al_fwrite32le(file, (*actorId)); } //Close a file al_fclose(file); } }
GameLogic::GameLogic(int Width, int Height, float LogicFPS) { Debug = false; Display = NULL; EventQueue = NULL; Timer = NULL; Bitmap.Logo = NULL; Bitmap.Title = NULL; Font.Small = NULL; Font.Medium = NULL; Font.Big = NULL; Sound.HitWall = NULL; Sound.HitPaddle = NULL; Sound.BackgroundMusic = NULL; UserDataFile = NULL; DisplayWidth = Width; DisplayHeight = Height; TimerStep = 1.0 / LogicFPS; bool key[2] = { false, false }; float alpha = 0; float colr = 0; float colg = 0; float colb = 0; int tick = 0; int menu = 0; int high_score = 0; bool restart = true; int score = 0, multi = 1, final_score = 1; bool game_over, redraw; char scoretxt[8] = ""; char multitxt[4] = ""; char final_scoretxt[16] = ""; char hightxt[16] = ""; // intro screen while (1) { ALLEGRO_EVENT ev; al_wait_for_event(EventQueue, &ev); if(ev.type == ALLEGRO_EVENT_TIMER) { // trigger redraw tick++; if(tick < 180) { colr = colg = colb = (0.5 * sin( (tick - 90) * ALLEGRO_PI / 180.0) + 0.5); alpha = 0; } else if(tick < 360) { alpha = (0.5 * sin( (tick + 90) * ALLEGRO_PI / 180.0) + 0.5); colr = colg = colb = 1; } else if(tick > 450 && tick < 630) { colr = colg = colb = alpha = (0.5 * cos( (tick - 90) * ALLEGRO_PI / 180.0) + 0.5); } else if(tick > 632) { break; } redraw = true; } else if(ev.type == ALLEGRO_EVENT_DISPLAY_CLOSE) { return; } else if(ev.type == ALLEGRO_EVENT_MOUSE_BUTTON_UP) { break; } if(redraw && al_is_event_queue_empty(EventQueue)) { redraw = false; al_clear_to_color(al_map_rgb_f(colr,colg,colb)); al_draw_tinted_bitmap(Bitmap.Logo, al_map_rgba_f(alpha, alpha, alpha, alpha), (DisplayWidth - al_get_bitmap_width(Bitmap.Logo) ) / 2, (DisplayHeight - al_get_bitmap_height(Bitmap.Logo) )/ 2, 0); al_flip_display(); } } al_flip_display(); al_clear_to_color(al_map_rgb(0,0,0)); // load hiscore info UserDataFile = al_fopen("assets/hiscore.txt", "r"); if(UserDataFile) { char *line = (char*) malloc (17); line = al_fgets(UserDataFile, line, 17); high_score = atoi(line); free(line); } else { high_score = 0; } final_score = 0; sprintf(hightxt, "High: %d", high_score); al_fclose(UserDataFile); // main menu loop while (1) { ALLEGRO_EVENT ev; al_wait_for_event(EventQueue, &ev); if(ev.type == ALLEGRO_EVENT_TIMER) { // trigger redraw redraw = true; } else if(ev.type == ALLEGRO_EVENT_DISPLAY_CLOSE) { // exit if the Display is closed return; } else if(ev.type == ALLEGRO_EVENT_MOUSE_AXES || ev.type == ALLEGRO_EVENT_MOUSE_ENTER_DISPLAY) { // track mouse movement if((ev.mouse.y > 350) && (ev.mouse.y < 388) && (ev.mouse.x > 250) && (ev.mouse.x < 388)) { menu = 1; } else { menu = 0; } } else if(ev.type == ALLEGRO_EVENT_MOUSE_BUTTON_UP) { // track mouse clicks if(menu == 1) { break; } } if(redraw && al_is_event_queue_empty(EventQueue)) { // process entire queue and only render if the Timer has ticked redraw = false; al_clear_to_color(al_map_rgb(0,0,0)); al_draw_bitmap(Bitmap.Title, (DisplayWidth - al_get_bitmap_width(Bitmap.Title) ) / 2, 16, 0); al_draw_text(Font.Small, al_map_rgb(128,128,0), DisplayWidth / 2, 250, ALLEGRO_ALIGN_CENTRE, "Version 0.1"); if(menu == 1) { al_draw_text(Font.Medium, al_map_rgb(128,0,0), DisplayWidth / 2, 350, ALLEGRO_ALIGN_CENTRE, "Play Now!"); } else { al_draw_text(Font.Medium, al_map_rgb(128,128,0), DisplayWidth / 2, 350, ALLEGRO_ALIGN_CENTRE, "Play Now!"); } al_flip_display(); } } // seed rand function srand(al_get_time()); // game wrapper lets us restart the game while(restart) { // hide the cursor //al_hide_mouse_cursor(Display); // assume we don't want to play again restart = false; // Base objects Ball StarterBall = Ball(); Paddle StarterPaddle = Paddle(0); Paddle StarterPaddle2 = Paddle(DisplayHeight - 8); Paddle StarterPaddle3 = Paddle(0, true); Paddle StarterPaddle4 = Paddle(DisplayWidth - 8, true); // TODO: load the right map // set Bitmap.Title // set base speed // create list of block entities // Populate entity lists // Remove static data when we get map loading done for(int i=0; i < 64; i++) { Brick SingleBlockyEntity = Brick((i % 16) * 40, 80 + ( (i / 16) * 10), i % 8); Bricks.push_back(SingleBlockyEntity); } Paddles.push_back(StarterPaddle); Paddles.push_back(StarterPaddle2); Paddles.push_back(StarterPaddle3); Paddles.push_back(StarterPaddle4); Balls.push_back(StarterBall); // save high score if the final score is higher if(final_score > high_score) { high_score = final_score; sprintf (hightxt, "High: %d", high_score); } // reset score if not a continuation // init hud // force initial draw redraw = true; // reset physics game_over = false; // main game loop while(1) { ALLEGRO_EVENT ev; al_wait_for_event(EventQueue, &ev); if(ev.type == ALLEGRO_EVENT_TIMER) { // game logic loop if(!game_over) { // calculate each ball's new position and check for... Path CurrentPath; for(int i = 0; i < Balls.size(); i++) { CurrentPath = Balls[i].GetPath(TimerStep); // distance traveled in axis directions in time delta t float NX = Balls[i].Position.DeltaX * TimerStep; float NY = Balls[i].Position.DeltaY * TimerStep; // new unencombered position float NewPositionX = Balls[i].Position.X + NX; float NewPositionY = Balls[i].Position.Y + NY; ClosestEntity Closest; Closest.Magnitude = 10000; // look for collisions for(int j = 0; j < (int)Bricks.size(); j++) { // only check items that haven't been hit in the most recent update if (!Bricks[j].HasBeenHit()) { Intersection Point = Bricks[j].PathIntersect(CurrentPath); if (Point.Type == Intersecting) { // mark this hit so we don't check for it again... // until the next update frame. Bricks[i].Hit = true; // see how far the collision is float Magnitude = sqrt(pow(Point.X - Balls[i].Position.X, 2) + pow(Point.Y - Balls[i].Position.Y, 2)); if (Magnitude < Closest.Magnitude) { // nab the closest collision so we can handle multiple collisions Closest.Magnitude = Magnitude; Closest.EntityIndex = j; Closest.CollisionPoint = Point; } } } } if (Closest.CollisionPoint.Type == Intersecting) { if(Bricks[Closest.EntityIndex].Harm(Balls[i].CollisionDamage)) { // TODO: play a sound // Update the ball to the point of collision Balls[i].Position.X = Closest.CollisionPoint.X; Balls[i].Position.Y = Closest.CollisionPoint.Y; // Reflect the ball in the appropriate direction switch(Closest.CollisionPoint.From) { default: case Top: case Bottom: Balls[i].ReflectY(); break; case Left: case Right: Balls[i].ReflectX(); break; } /* Find the fraction of DeltaT that had to occur before this collision float UnknownT = TimerStep * (Closest.Magnitude / sqrt(pow(NX,2) + pow(NY, 2))); // simlulate the ball movement after the collision Balls[i].GetPath(TimerStep - UnknownT); break;*/ } if(NewPositionX > 0.0f && NewPositionX < DisplayWidth - Balls[i].Width && NewPositionY > 0.0f && NewPositionY < DisplayHeight - Balls[i].Height) { Balls[i].Live = true; Balls[i].Position.X = NewPositionX; Balls[i].Position.Y = NewPositionY; } else { Balls[i].Live = false; } } // make sure at least one ball is in play if(Balls[0].BallCount < 1) { game_over = true; } } } else { // end game animation logic } // trigger a render every tick redraw = true; } else if(ev.type == ALLEGRO_EVENT_DISPLAY_CLOSE) { break; } else if(ev.type == ALLEGRO_EVENT_MOUSE_AXES || ev.type == ALLEGRO_EVENT_MOUSE_ENTER_DISPLAY) { if(!game_over) { // update each paddle for(int i = 0; i < Paddles.size(); i++) { // horizontal clip if(Paddles[i].IsHorizontal()) { if(ev.mouse.x < (Paddles[i].Width / 2) ) { Paddles[i].Position.X = 0; } else if(ev.mouse.x > DisplayWidth - (Paddles[i].Width / 2) ) { Paddles[i].Position.X = DisplayWidth - Paddles[i].Width; } else { Paddles[i].Position.X = ev.mouse.x - (Paddles[i].Width / 2); } } else { // vertical clip if(ev.mouse.y < (Paddles[i].Height / 2) ) { Paddles[i].Position.Y = 0; } else if(ev.mouse.y > DisplayHeight - (Paddles[i].Height / 2) ) { Paddles[i].Position.Y = DisplayHeight - Paddles[i].Height; } else { Paddles[i].Position.Y = ev.mouse.y - (Paddles[i].Height / 2); } } } } else { // track mouse movement for retry menu logic } } else if(ev.type == ALLEGRO_EVENT_MOUSE_BUTTON_UP) { if(game_over) { // track mouse clicks for retry menu logic } else { // Debug // add a ball at the current mouse position } } else if(ev.type == ALLEGRO_EVENT_KEY_DOWN) { // set keys we are interested in if pressed switch(ev.keyboard.keycode) { case ALLEGRO_KEY_ESCAPE: key[KEY_ESC] = true; break; case ALLEGRO_KEY_SPACE: key[KEY_SPACE] = true; break; } } else if(ev.type == ALLEGRO_EVENT_KEY_UP) { // release keys we are interested in if let go switch(ev.keyboard.keycode) { case ALLEGRO_KEY_ESCAPE: key[KEY_ESC] = false; break; case ALLEGRO_KEY_SPACE: key[KEY_SPACE] = false; break; } } if(redraw && al_is_event_queue_empty(EventQueue)) { //The render loop redraw = false; if(!game_over) { // game play // blank al_clear_to_color(al_map_rgb(128,128,128)); // render each brick for(int i = 0; i < Bricks.size(); i++) { Bricks[i].Render(); } // render each ball for(int i = 0; i < Balls.size(); i++) { Balls[i].Render(); } // render each paddle for(int i = 0; i < Paddles.size(); i++) { Paddles[i].Render(); } // render HUD sprintf (scoretxt, "%d", score); sprintf (multitxt, "%d", multi); al_draw_text(Font.Medium, al_map_rgb(0,0,0), 8, 8, ALLEGRO_ALIGN_LEFT, "Score"); al_draw_text(Font.Medium, al_map_rgb(255,255,255), 128, 8, ALLEGRO_ALIGN_LEFT, scoretxt); al_draw_text(Font.Medium, al_map_rgb(0,0,0), 300, 8, ALLEGRO_ALIGN_LEFT, "x"); al_draw_text(Font.Medium, al_map_rgb(255,255,255), 316, 8, ALLEGRO_ALIGN_LEFT, multitxt); al_draw_text(Font.Medium, al_map_rgb(0,0,0), 424, 8, ALLEGRO_ALIGN_LEFT, hightxt); } else { // render retry menu al_clear_to_color(al_map_rgb(128,128,128)); } // always flip Display al_flip_display(); } } } // save high score if the final score is higher if(final_score > high_score) { high_score = final_score; } sprintf (hightxt, "%d", high_score); // write highscore to file UserDataFile = al_fopen("assets/hiscore.txt", "w"); if(UserDataFile) { al_fputs(UserDataFile, hightxt); al_fclose(UserDataFile); } // proper cleanup is important! al_destroy_bitmap(Bitmap.Title); al_destroy_bitmap(Bitmap.Logo); al_destroy_timer(Timer); al_destroy_display(Display); al_destroy_event_queue(EventQueue); }
bool ConfigFile::Save( std::string Filename ) { ALLEGRO_FILE* fileHnd; std::string document; bool dataNum; std::string* escstr; fileHnd = al_fopen( Filename.c_str(), "w" ); if( fileHnd == 0 ) return false; document.clear(); for( std::list<ConfigData*>::iterator i = Contents.begin(); i != Contents.end(); i++ ) { ConfigData* cd = (ConfigData*)(*i); document.append( cd->Key->c_str() ); if( cd->IsArray ) { document.append( " [ " ); bool isFirst = true; for( std::vector<std::string*>::iterator s = cd->Contents->begin(); s != cd->Contents->end(); s++ ) { if( isFirst ) isFirst = false; else document.append( ", " ); std::string* cs = (std::string*)(*s); dataNum = IsNumber( *cs ); if( !dataNum ) { document.append( "\"" ); escstr = EscapeString(*cs); document.append( escstr->c_str() ); delete escstr; document.append( "\"" ); } else { document.append( cs->c_str() ); } } document.append( " ]\n" ); } else { document.append( " = " ); dataNum = IsNumber( *cd->Contents->front() ); if( !dataNum ) { document.append( "\"" ); escstr = EscapeString(*cd->Contents->front()); document.append( escstr->c_str() ); delete escstr; document.append( "\"" ); } else { document.append( cd->Contents->front()->c_str() ); } document.append( "\n" ); } } al_fputs( fileHnd, document.c_str() ); al_fclose( fileHnd ); return true; }