Logo Search packages:      
Sourcecode: kball version File versions  Download package

gkernel.cpp

// -----------------------------------------------
// KBall Game
// By Kronoman
// Copyright (c) 2003,2004,2005, Kronoman
// In loving memory of my father
// -----------------------------------------------
// gkernel.h
// -----------------------------------------------
// Game main loop and kernel
// -----------------------------------------------

#include "gkernel.h" // kernel header

#include "gerror.h" // to show any error that may arise
#include "misc_def.h" // some definitions
#include "filehelp.h"
#include "savescrs.h" // for screenshoots
#include "ui_misc.h"

// -----------------------------------------------
// Global variables
// -----------------------------------------------
// Counter for the main timer
static volatile int speed_counter = 0;

// this 2 counters are for count the fps
static volatile int frame_count, fps;

// ---- End of global variables declaration ----

// -----------------------------------------------
// Timing stuff
// -----------------------------------------------
// Timer callback for the speed counter
void increment_speed_counter()
{
      speed_counter++;
}

END_OF_FUNCTION(increment_speed_counter);

// Timer callback for measuring the frames per second
void fps_proc(void)
{
      fps = frame_count;
      frame_count = 0;
}

END_OF_FUNCTION(fps_proc);

// ==========================================================================
// KERNEL CLASS ITSELF BELOW THIS
// ==========================================================================

int CGameKernel::timer_installed_count = 0; // how many times we installed the timer; when this is 0, remove/install timers

// -------------------------------------
// Constructor
// -------------------------------------
CGameKernel::CGameKernel()
{
      mtracer.add("CGameKernel::CGameKernel()");

      game_over = false;

      dbuffer = NULL; // doble buffer

      backdropbmp = NULL; // background bitmap for current level

      current_level_file_name[0] = '\0';
      
      stats.reset(); // reset stats
}

// -------------------------------------
// Destructor
// -------------------------------------
CGameKernel::~CGameKernel()
{
      mtracer.add("CGameKernel::~CGameKernel()");

      this->shutdown();
}

// -------------------------------------------------------------------
// Initializes game (sets timers, loads data, etc)
// -------------------------------------------------------------------
void CGameKernel::init()
{
      mtracer.add("CGameKernel::init()");

      if (dbuffer == NULL)
            dbuffer = create_bitmap(SCREEN_W, SCREEN_H); // create doble buffer bitmap

      if (dbuffer == NULL)
            raise_error("CGameKernel::init()\nERROR: unable to create doble buffer bitmap!\n");

      clear_bitmap(screen);

      textout_centre_ex(screen, font, "[   Please wait... loading...   ]", SCREEN_W / 2, SCREEN_H / 2, makecol(255, 255, 255), makecol(0, 0, 64));

      // Load general data for the game
      char tmp_file_buf[2048];

      if (game_map.load_tile_set_from_file(where_is_the_filename(tmp_file_buf, TILESET_FILE)) )
            raise_error("CGameKernel::init()\nERROR!\nCan't load  \"%s\" file!\n", TILESET_FILE); // load tileset

      if (main_data_file.load_datafile(where_is_the_filename(tmp_file_buf, SPRITES_FILE)) )
            raise_error("CGameKernel::init()\nERROR!\nUnable to load \"%s\" file!\n", SPRITES_FILE);

      mtracer.add("\tGeneral data loaded OK");

      // find the ball's sprite texture and mask
      player_ball.spr = (BITMAP *)main_data_file.get_resource_dat("BALL");

      if (player_ball.spr == NULL)
            raise_error("CGameKernel::init()\nERROR! Can't find BALL sprite!\n");

      player_ball.spr_shadow = (BITMAP *)main_data_file.get_resource_dat("BALL_MASK_SHADOW");

      if (player_ball.spr_shadow == NULL)
            raise_error("CGameKernel::init()\nERROR! Can't find BALL_MASK_SHADOW sprite!\n");

      mtracer.add("\tBall data loaded OK");

      // find GUI fonts
      game_time_font = (FONT *)main_data_file.get_resource_dat("GAME_TIME_FONT");

      game_score_font = (FONT *)main_data_file.get_resource_dat("GAME_SCORE_FONT");

      game_messages_font = (FONT *)main_data_file.get_resource_dat("GAME_MESSAGES_FONT");

      // set some stuff
      game_map.timer_tick_rate = GKERNEL_FPSQ; // set timer ratio for map

      // set the timers
      if (timer_installed_count == 0)
      {
            LOCK_VARIABLE(speed_counter);
            LOCK_FUNCTION((void *)increment_speed_counter);

            LOCK_VARIABLE(fps);
            LOCK_VARIABLE(frame_count);
            LOCK_FUNCTION((void *)fps_proc);

            // install the timer
            if (install_int_ex(increment_speed_counter, BPS_TO_TIMER(GKERNEL_FPSQ)))
                  raise_error("CGameKernel::init() : Can't install timer at %d bps\n", GKERNEL_FPSQ);

            if (install_int(fps_proc, 1000))
                  raise_error("CGameKernel::init() : Can't install timer to measure fps.");

            mtracer.add("\tTimer installed for first time");
      }

      timer_installed_count++; // just to make sure that nobody else will remove our precious timer until we are ready too

      mtracer.add("\ttimer_installed_count = %d", timer_installed_count);

      // here start all the variables and all stuff
      game_over = false;
      frame_count = fps = 0;

      current_level_file_name[0] = '\0';
}

// -------------------------------------------------------------------
// shuts down game (unsets timers, unloads data, etc)
// -------------------------------------------------------------------
void CGameKernel::shutdown()
{
      mtracer.add("CGameKernel::shutdown()");

      // remove timer(s), only if active
      if (timer_installed_count > 0)
      {
            timer_installed_count--;

            mtracer.add("\ttimer_installed_count = %d", timer_installed_count);

            if (timer_installed_count == 0)
            {
                  remove_int(increment_speed_counter);
                  remove_int(fps_proc);
                  timer_installed_count = 0;

                  mtracer.add("\tTimer totally removed");
            }
      }

      // FREE THE RAM USED
      game_map.free_memory();

      main_data_file.nuke_datafile();

      particle_manager.nuke_particle_list();

      if (dbuffer != NULL)
      {
            destroy_bitmap(dbuffer);
            dbuffer = NULL;
      }

      if (backdropbmp != NULL)
      {
            destroy_bitmap(backdropbmp);
            backdropbmp = NULL;
      }

      current_level_file_name[0] = '\0';
      
      music_loader.free_memory();
      
      background_loader.free_memory();
}

// -------------------------------------------------------------------
// This is the main game loop
//
// **NOTICE**
// YOU **MUST** CALL IN THIS ORDER:
//   gkernel.init();
//   gkernel.load_level_file("level.map");
//   gkernel.game_loop();
//   gkernel.shutdown();
//
// Will return CBALL_IS_DEAD, or CBALL_EXIT_LEVEL (dead, or won level)
//
// **NOTE** THE SPECIAL RETURN VALUE 'GKERNEL_USER_FINISHED_GAME' MEANS THAT THE PLAYER
// DON'T WANT TO PLAY (HE HIT ESC, SO ABORT THE GAME)
// -------------------------------------------------------------------
int CGameKernel::game_loop()
{
      int ret = 0;
      mtracer.add("CGameKernel::game_loop()");

      // Main game loop
      game_over = false; // we start just now :P

      speed_counter = 0;

      // play music
      soundw.music_start();
      
      while (game_over == false || ret == 0) // don't go out if this particular game is not over, and if ret don't has a valid value (!= 0)
      {
            while (speed_counter > 0)
            {
                  ret = this->update_logic(); // update game logic
                  speed_counter--;

                  if (key[KEY_ESC])
                  {
                        game_over = true;
                        ret = GKERNEL_USER_FINISHED_GAME;
                  } // with ESC you end the game

                  if (ret == CBALL_IS_DEAD || ret == CBALL_EXIT_LEVEL)
                        game_over = true; // end of this game session
            }

            this->update_screen(); // update screen

            frame_count++; // count fps

            if (key[KEY_F12]) // with F12 you take a screenshoot
            {
                  soundw.music_pause();
                  
                  // watermark
                  textout_right_ex(screen, font, "(C) 2004, Kronoman", SCREEN_W, SCREEN_H - text_height(font), makecol(64, 64, 64),-1);

                  save_screenshoot("kball", 0, "bmp"); // save

                  // add confirmation message

                  ui_misc_text_out_shadow(screen, font, "Screenshoot saved", SCREEN_W / 2, SCREEN_H / 2, makecol(0, 0, 0), makecol(255, 255, 255));
                  clear_keybuf();
                  rest(800);
                  clear_keybuf();
                  this->update_screen();
                  speed_counter = 0;
                  
                  soundw.music_resume();
            }

            if (key[KEY_ESC] || ret == GKERNEL_USER_FINISHED_GAME ) // with ESC you end the program
            {
                  soundw.music_pause();
                  
                  // add confirmation message
                  ui_misc_dark_bmp(screen);
                  ui_misc_text_out_shadow(screen, game_messages_font, "END GAME? Y/N", SCREEN_W / 2, SCREEN_H / 2, makecol(0, 0, 0), makecol(0, 255, 255));
                  clear_keybuf();
                  rest(25);
                  clear_keybuf();

                  if ((readkey() >> 8) == KEY_Y)
                  {
                        game_over = true;
                        ret = GKERNEL_USER_FINISHED_GAME;
                  }
                  else
                  {
                        game_over = false;
                        ret = 0; // abort the exit  -- DEBUG this, may be the misterious 0 value returned... :P
                  }

                  this->update_screen();

                  speed_counter = 0;
                  
                  soundw.music_resume();
            }


      } // end of main game loop

      
      soundw.music_stop();
      
      return ret;
}

// -------------------------------------------------------------------
// this updates 1 logic update of game ; also has code to PAUSE the game (KEY P or PAUSE)
// will return CBALL_IS_DEAD, CBALL_IS_FINE, or CBALL_EXIT_LEVEL
// -------------------------------------------------------------------
int CGameKernel::update_logic()
{
      int ret = CBALL_IS_FINE;
      
      // statistics
      
      // para contar el tiempo
      static int last_t_s = 0;
      if (++last_t_s > GKERNEL_FPSQ)
      {
            stats.add_time(0,0,1);
            last_t_s = 0;     
      }
      
      stats.score = player_ball.score;
      // end statistics

      if (key[KEY_PAUSE] || key[KEY_P]) // - pause the game -
      {
            soundw.music_pause();
            
            ui_misc_dark_bmp(screen);

            ui_misc_text_out_shadow(screen, game_messages_font, "- PAUSE -", SCREEN_W / 2, SCREEN_H / 2, makecol(0, 0, 0), makecol(0, 255, 255));

            ui_misc_wait_for_input();

            this->update_screen();
            speed_counter = 0;
            
            soundw.music_resume();
      }

      game_map.update_logic();
      particle_manager.update_logic();

      ret = player_ball.update_logic(); // return value

      if (game_map.time_m*60 + game_map.time_s <= 10) // we are running low in time!, put a warning count
      {
            static int old_time = 0;

            if (old_time != game_map.time_s) // only each second
            {
                  old_time = game_map.time_s;

                  char tmp_str[80];
                  usprintf(tmp_str, "%d", game_map.time_s);

                  particle_manager.add_particle(new CExplosiveTextParticle(player_ball.x + player_ball.spr->w / 2, player_ball.y, (float)(rand() % 100 - 50) / 100.0, (float)((rand() % 300) + 150) / -100.0, makecol(rand() % 55 + 200, 0, 0), rand() % 15 + 25, tmp_str, game_time_font));
            }
      }

      if (game_map.time_m*60 + game_map.time_s <= 0) // time out, we are so DEAD!
      {
            ret = CBALL_IS_DEAD;
      }

      if (ret == CBALL_EXIT_LEVEL) // end of level :^D
      {
            if (game_map.prize_map_indispensable > 0) // we can't exit if we have prizes left.
            {
                  // the user can't exit now, so drop a visual text saying it
                  if (rand() % 100 < 40)
                        particle_manager.add_particle(new CTextParticle(player_ball.x + player_ball.spr->w / 2, player_ball.y + player_ball.spr->h / 2, (float)(rand() % 300 - 150) / 100.0, (float)(rand() % 300) / -100.0, makecol(rand() % 55 + 200, 0, 255), rand() % 25 + 15, string("CAN'T EXIT"), font));

                  ret = CBALL_IS_FINE; // abort level exit
            }

      }
      
      soundw.music_poll();

      return ret;
}

// -------------------------------------------------------------------
// This updates the screen
// -------------------------------------------------------------------
void CGameKernel::update_screen()
{
      int xc, yc; // x y of camera
      static int blink_counter = 0; // counter for blink text timer... :P
      static bool show_fps = false; // show fps ? F11 turns it
      
      if (key[KEY_F11]) // show fps
            show_fps = !show_fps;
      
      blink_counter++;

      if (blink_counter > GKERNEL_FPSQ)
            blink_counter = 0;

      // camera position
      xc = (int)player_ball.x - SCREEN_W / 2;

      yc = (int)player_ball.y - SCREEN_H / 2;

      if (xc < 0)
            xc = 0;

      if (yc < 0)
            yc = 0;

      if (xc > TMAP_SIZE*TMAPS_W - SCREEN_W)
            xc = TMAP_SIZE * TMAPS_W - SCREEN_W;

      if (yc > TMAP_SIZE*TMAPS_H - SCREEN_H)
            yc = TMAP_SIZE * TMAPS_H - SCREEN_H;


      blit(backdropbmp, dbuffer, 0, 0, 0, 0, backdropbmp->w, backdropbmp->h); // blit background

      //text_mode( -1); // transparent background for text

      
      if (show_fps)
      {
            textprintf_ex(dbuffer, font, 0, SCREEN_H - text_height(font), makecol(255, 255, 255), -1, "%4d fps", fps);
            textout_ex(dbuffer, font, "Copyright (c) 2003, 2004, 2005, Kronoman", 0, SCREEN_H - text_height(font)*3, makecol(128, 128, 255),-1);
      }

      // ---- 3D scene (this items are draw using the Allegro's 3D software polygon routines)

      clear_scene(dbuffer); // clear 3D scene zbuffer

      player_ball.draw(dbuffer, xc, yc); // draw ball

      game_map.draw_map(dbuffer, xc, yc, SCREEN_W, SCREEN_H, 0, false); // draw map

      game_map.draw_map(dbuffer, xc, yc, SCREEN_W, SCREEN_H, 1, false); // draw top layer (the one with prizes =D)

      render_scene(); // render the 3D scene to the bitmap


      // ---- end of 3D scene

      // draw particles, they are 2D
      particle_manager.render(dbuffer, xc, yc);

      // time left message
      textprintf_centre_ex(dbuffer, game_time_font, dbuffer->w / 2 + 3, 3, (game_map.time_m*60 + game_map.time_s > 10) ? makecol(100, 0, 128) : makecol(128, 0, 0),-1,"%02d:%02d", game_map.time_m, game_map.time_s);

      textprintf_centre_ex(dbuffer, game_time_font, dbuffer->w / 2, 0, (game_map.time_m*60 + game_map.time_s > 10) ? makecol(200, 0, 255) : makecol(255, 0, 0),-1, "%02d:%02d", game_map.time_m, game_map.time_s);

      // score message
      textprintf_ex(dbuffer, game_score_font, 3, 3, makecol(128, 100, 0), -1,"%010ld", player_ball.score);

      textprintf_ex(dbuffer, game_score_font, 0, 0, makecol(255, 200, 0), -1,"%010ld", player_ball.score);

      // prizes left message
      if (game_map.prize_map_indispensable > 0)
      {
            textprintf_right_ex(dbuffer, game_score_font, dbuffer->w + 3, 3, makecol(0, 64, 128),-1, "%04d", game_map.prize_map_indispensable);
            textprintf_right_ex(dbuffer, game_score_font, dbuffer->w, 0, makecol(0, 128, 255),-1, "%04d", game_map.prize_map_indispensable);
      }
      else
      {
            int cc1, cc2; // text color for blink text
            // the EXIT blinks... :D
            if (blink_counter < GKERNEL_FPSQ / 2)
            {
                  cc1 = makecol(0, 100, 128);
                  cc2 = makecol(0, 200, 255);
            }
            else
            {
                  cc1 = makecol(128, 100, 0);
                  cc2 = makecol(255, 200, 0);

            }

            textprintf_right_ex(dbuffer, game_score_font, dbuffer->w + 3, 3, cc1,-1, "EXIT");
            textprintf_right_ex(dbuffer, game_score_font, dbuffer->w, 0, cc2,-1, "EXIT");
      }

      // lives left message
      textprintf_ex(dbuffer, game_score_font, 0 + 3, (int)(text_height(game_score_font)*1.05) + 3, makecol(128, 110, 0),-1, "Balls:%2d", player_ball.lives);

      textprintf_ex(dbuffer, game_score_font, 0, (int)(text_height(game_score_font)*1.05), makecol(255, 220, 0),-1, "Balls:%2d", player_ball.lives);

      // debug, remove this!
      //textprintf(dbuffer, font,0,0,makecol(255,255,0), "x%3f,y%3f", player_ball.anglex,player_ball.angley);

      
      //stats.print(dbuffer, 50, makecol(255,255,255), -1, font); // DEBUG -- REMOVE THIS OR MAKE IT TOGGEABLE WITH A KEY
      
      blit(dbuffer, screen, 0, 0, 0, 0, dbuffer->w, dbuffer->h); // dump the render to screen

      // yield_timeslice(); // play nice with multitask
      //rest(0); // the way of playing nice with multitask since Allegro 4.1.15 WIP
}

// -------------------------------------------------------------------
// Helper function, takes a bitmap, and returns a new allocated bitmap
// with the 1st bitmap tiled in the size of the screen
// this serves for the background imagen
// -------------------------------------------------------------------
BITMAP *CGameKernel::tile_bmp_to_screen_size(BITMAP *bmp)
{
      BITMAP *b = NULL;

      if (bmp == NULL)
            raise_error("CGameKernel::tile_bmp_to_screen_size()\nERROR: bitmap null; can't tile it!\n");

      b = create_bitmap(SCREEN_W, SCREEN_H);

      if (b == NULL)
            raise_error("CGameKernel::tile_bmp_to_screen_size()\nERROR: unable to create bitmap!\n");

      for (int y = 0; y < SCREEN_H + bmp->h; y += bmp->h)
            for (int x = 0; x < SCREEN_W + bmp->w; x += bmp->w)
                  blit(bmp, b, 0, 0, x, y, bmp->w, bmp->h);

      return b;
}

// -----------------------------------------------------------------------
// Loads a level from a file, and sets the game ready to play on that level
//
// *MUST* BE CALLED BEFORE STARTING THE GAME LOOP!
// WILL LOAD THE MAP, BACKGROUND, etc
// -----------------------------------------------------------------------
void CGameKernel::load_level_file(const char *file)
{
      mtracer.add("CGameKernel::load_level_file(\"%s\")", file);

      if (game_map.load_map_from_file(file))
            raise_error("CGameKernel::load_level_file()\nERROR!\nCan't load level '%s'!\n", file);

      usprintf(current_level_file_name, "%s", file); // save current file name

      // set the player position and properties
      player_ball.x = (game_map.sxp * TMAPS_W) + (TMAPS_W - BALL_RADIUS * 2) / 2;

      player_ball.y = (game_map.syp * TMAPS_H) + (TMAPS_H - BALL_RADIUS * 2) / 2;

      player_ball.z = 1.0;

      player_ball.dx = player_ball.dy = player_ball.dz = 0.0;

      player_ball.ctmap = &game_map;

      player_ball.particle_manager = &particle_manager;

      particle_manager.nuke_particle_list(); // CLEAN THE PARTICLES FOR THE NEW LEVEL :P

      // DEBUG -- BACKGROUND! - DECIDIR QUE CORNO HAGO CON ESTO
      // Load the background from info stored on the level map
      if (backdropbmp != NULL)
      {
            destroy_bitmap(backdropbmp);
            backdropbmp = NULL;
      }

      backdropbmp = tile_bmp_to_screen_size( background_loader.get_background("backgr.dat", game_map.background_index) );

      mtracer.add("\tLoaded background (%d) of level OK", game_map.background_index);
      
      // load music
      soundw.music_load(music_loader.get_music("music_l.dat", game_map.music_index));
      mtracer.add("\tLoaded music (%d) of level OK", game_map.music_index);
}


// -----------------------------------------------------------------------
// This will start a single level game
// Will return CBALL_IS_DEAD(totally DEAD, full game over, 0 lives)
// CBALL_EXIT_LEVEL (only useful for campaign), or GKERNEL_USER_FINISHED_GAME if player cancelled game
// -----------------------------------------------------------------------
int CGameKernel::play_a_single_level(char *level_filename)
{
      int ret;
      bool do_the_loop = true; // loop, for loading level many times, etc
      int old_score = 0; // this is for restoring the score when the player loses a ball

      mtracer.add("\nCGameKernel::play_a_single_level('%s');\n", level_filename);

      this->init(); // init game kernel

      old_score = player_ball.score;

      while (do_the_loop)
      {
            mtracer.add("\tLoading and playing level.");

            this->load_level_file(level_filename);

            ret = this->game_loop();

            mtracer.add("\t--> this->game_loop = %4d\n", ret);

            // according to ret value, show the player, if we won, we lose, or if we have lives to keep playing, etc
            // the { } in the case <x>: are there to force the scope, so don't remove them (I know what I'm doing...)
            switch (ret)
            {

                  case CBALL_EXIT_LEVEL:
                  {
                        // the player won the level, OK, so we exit
                        // this value will be used by campaign function, if available

                        this->update_screen();

                        ui_misc_dark_bmp(screen);
                        ui_misc_text_out_shadow(screen, game_messages_font, "YOU WON THE LEVEL!", SCREEN_W / 2, SCREEN_H / 2, makecol(0, 0, 0), makecol(0, 255, 255));

                        // GIVE BONUS
                        int bonus = 0;
                        //text_mode(makecol(0, 0, 64));

                        clear_keybuf();
                        rest(550);
                        clear_keybuf();

                        rectfill(screen, 0, SCREEN_H / 2 + text_height(game_messages_font), SCREEN_W, SCREEN_H, makecol(0, 0, 64));


                        textout_centre_ex(screen, game_messages_font, "TIME BONUS", SCREEN_W / 2, SCREEN_H / 2 + text_height(game_messages_font), makecol(0, 255, 255),makecol(0,0,64));

                        for (int i = 0; i < game_map.time_m*60 + game_map.time_s; i++)
                        {
                              textprintf_centre_ex(screen, game_messages_font, SCREEN_W / 2, SCREEN_H / 2 + text_height(game_messages_font)*2, makecol(0, 255, 255),makecol(0,0,64), "   %04d x 100 = %07d   ", i, bonus);
                              bonus += 100; // 100 bonus points for each second

                              // DEBUG -- falta SONIDO!

                              if (keypressed() || mouse_b || joy[0].button[0].b)
                                    break; // let the user cancel the count

                              poll_joystick();

                              if (keyboard_needs_poll())
                                    poll_keyboard();

                              if (mouse_needs_poll())
                                    poll_mouse();

                              rest(10);
                        }

                        // give real bonus
                        bonus = (game_map.time_m * 60 + game_map.time_s) * 100;

                        player_ball.score += bonus;
                        stats.score = player_ball.score;

                        textprintf_centre_ex(screen, game_messages_font, SCREEN_W / 2, SCREEN_H / 2 + text_height(game_messages_font)*2, makecol(0, 255, 255), makecol(0,0,64),"   %04d x 100 = %07d   ", game_map.time_m*60 + game_map.time_s, bonus);

                        textprintf_centre_ex(screen, game_messages_font, SCREEN_W / 2, SCREEN_H / 2 + text_height(game_messages_font)*3, makecol(0, 255, 255), makecol(0,0,64),"Score = %010ld", player_ball.score);

                        //text_mode( -1);

                        textout_centre_ex(screen, font, "-- Hit any key to continue --", SCREEN_W / 2, SCREEN_H - text_height(font), makecol(255, 255, 255),-1);

                        ui_misc_wait_for_input();

                        do_the_loop = false;
                  }

                  break; // CBALL_EXIT_LEVEL


                  case CBALL_IS_DEAD:
                  {
                        this->update_screen();

                        ui_misc_dark_bmp(screen);

                        if (game_map.time_m*60 + game_map.time_s > 0)
                              ui_misc_text_out_shadow(screen, game_messages_font, "YOU LOST THE BALL!", SCREEN_W / 2, SCREEN_H / 2, makecol(0, 0, 0), makecol(0, 255, 255));
                        else
                              ui_misc_text_out_shadow(screen, game_messages_font, "TIME UP!", SCREEN_W / 2, SCREEN_H / 2, makecol(0, 0, 0), makecol(0, 255, 255));

                        textout_centre_ex(screen, font, "-- Hit any key to continue --", SCREEN_W / 2, SCREEN_H - text_height(font), makecol(255, 255, 255),-1);

                        ui_misc_wait_for_input();

                        // the player died, rest one live
                        player_ball.lives--;
                        stats.blost++; // statistics
                        stats.score = player_ball.score;

                        player_ball.anglex = player_ball.angley = 0.0; // reset ball rotations

                        if (player_ball.lives < 0) // the player is dead. 0 lives. GAME OVER. :(
                        {

                              BITMAP *bmp_game_over = (BITMAP *)main_data_file.get_resource_dat("GAME_OVER_BMP");

                              blit(bmp_game_over, screen, 0, 0, 0, 0, bmp_game_over->w, bmp_game_over->h);
                              
                              stats.score = player_ball.score;
                              stats.print(screen, 150, makecol(255,255,0), -1, game_score_font); // print stats

                              // sound
                              SAMPLE *game_over_smp = (SAMPLE *)main_data_file.get_resource_dat("GAME_OVER_WAV");
                              play_sample(game_over_smp,255,128,1000,0);

                              textout_centre_ex(screen, font, "-- Hit any key to continue --", SCREEN_W / 2, SCREEN_H - text_height(font), makecol(128, 0, 0),-1);

                              ui_misc_wait_for_input();

                              do_the_loop = false;
                              
                              stats.reset(); // reset stats
                        }
                        else
                        {
                              // perdio una vida, pero le queda, jugar de nuevo el nivel
                              do_the_loop = true; // load the next level, damn
                              player_ball.score = old_score; // restore score
                        }

                  }

                  break; // end of CBALL_IS_DEAD

                  case GKERNEL_USER_FINISHED_GAME:
                  do_the_loop = false; // the player hit <ESC>, end of game :(
                  break; // GKERNEL_USER_FINISHED_GAME

                  default:  // it should NEVER come here
                  raise_error("ERROR at CGameKernel::play_a_single_level\nGuru meditation:\ngame_loop returned a invalid value (ret = %d)\n", ret);
                  break;
            }

      }

      this->shutdown(); // shutdown this beauty...

      return ret;
}


// -----------------------------------------------------------------------
// This will start a full campaign game
// -----------------------------------------------------------------------
void CGameKernel::play_a_full_campaign(char *level_filename)
{
      char tmp_str[4096]; // buffer to handle level filenames
      int current_level = 1; // current level number
      bool finished = false;
      int ret = 0;

      mtracer.add("\nCGameKernel::play_a_full_campaign('%s')\n", level_filename);

      while (!finished)
      {
            // load and play level
            usprintf(tmp_str, "%s#%d_MAP", level_filename, current_level);
            fix_filename_slashes(tmp_str);

            // check if the level exits before trying to play it -- DEBUG -- slow code here! do some better
            PACKFILE *fp_test = pack_fopen(tmp_str, F_READ);

            if (fp_test == NULL)
            {
                  // EL NIVEL NO EXISTE, LISTO, GANO

                  // the player won the campaign
                  
                  // NOTA: dado que cuando llega aca, ya hizo el this->shutdown, los .DAT estan DESCARGADOS
                  // por eso, recargo el bitmap y font en RAM y el sonido...
                  
                  clear_bitmap(screen);
                  textout_centre_ex(screen, font, "[   Please wait... loading...   ]", SCREEN_W / 2, SCREEN_H / 2, makecol(255, 255, 255), makecol(0, 0, 64));
                  
                  DATAFILE *dattmp = load_datafile_object(SPRITES_FILE, "WON_BMP");
                  if (!dattmp)
                        raise_error("CGameKernel::play_a_full_campaign\nCan't load %s -> WON_BMP\n", SPRITES_FILE);
                  BITMAP *bmp_won = (BITMAP *)dattmp->dat;

                  DATAFILE *dattmp2 = load_datafile_object(SPRITES_FILE, "GAME_SCORE_FONT");
                  if (!dattmp2)
                        raise_error("CGameKernel::play_a_full_campaign\nCan't load %s -> GAME_SCORE_FONT\n", SPRITES_FILE);
                  FONT *fs = (FONT *)dattmp2->dat;
                  
                  
                  blit(bmp_won, screen, 0, 0, 0, 0, bmp_won->w, bmp_won->h);
                              
                  stats.print(screen, 150, makecol(0,0,255), -1, fs);

                  // sound
                  DATAFILE *dattmp3 = load_datafile_object(SPRITES_FILE, "WON_WAV");
                  if (dattmp3)
                  {
                        play_sample((SAMPLE *)dattmp3->dat,255,128,1000,0);
                  }

                  textout_centre_ex(screen, font, "-- Hit any key to continue --", SCREEN_W / 2, SCREEN_H - text_height(font), makecol(255, 255, 0),-1);

                  ui_misc_wait_for_input();
                  
                  unload_datafile_object(dattmp); // bye bye bitmap... :)
                  unload_datafile_object(dattmp2); // bye bye font... :)
                  if (dattmp3)
                        unload_datafile_object(dattmp3); // bye bye sample
                  
                  dattmp = NULL;
                  
                  stats.reset();

                  return ; // chau... finalizo!
            }

            pack_fclose(fp_test);

            ret = this->play_a_single_level(tmp_str);

            mtracer.add("\t--> this->play_a_single_level('%s') = %4d\n", tmp_str, ret);

            if (ret == CBALL_EXIT_LEVEL) // exit level!
            {
                  current_level++;
                  finished = false;
            }
            else
            {
                  finished = true; // any other value means GAME OVER, sorry d00d, insert coin :P
            }

      }

}


Generated by  Doxygen 1.6.0   Back to index