Store user data in $HOME
[kball.git] / src / gkernel.cpp
blob1b10bae162db211cdd9872e78fb999d155dba48d
1 // -----------------------------------------------
2 // KBall Game
3 // By Kronoman
4 // Copyright (c) 2003,2004,2005, Kronoman
5 // In loving memory of my father
6 // -----------------------------------------------
7 // gkernel.h
8 // -----------------------------------------------
9 // Game main loop and kernel
10 // -----------------------------------------------
12 #include "gkernel.h" // kernel header
14 #include "gerror.h" // to show any error that may arise
15 #include "misc_def.h" // some definitions
16 #include "filehelp.h"
17 #include "savescrs.h" // for screenshoots
18 #include "ui_misc.h"
20 // -----------------------------------------------
21 // Global variables
22 // -----------------------------------------------
23 // Counter for the main timer
24 static volatile int speed_counter = 0;
26 // this 2 counters are for count the fps
27 static volatile int frame_count, fps;
29 // ---- End of global variables declaration ----
31 // -----------------------------------------------
32 // Timing stuff
33 // -----------------------------------------------
34 // Timer callback for the speed counter
35 void increment_speed_counter()
37 speed_counter++;
40 END_OF_FUNCTION(increment_speed_counter);
42 // Timer callback for measuring the frames per second
43 void fps_proc(void)
45 fps = frame_count;
46 frame_count = 0;
49 END_OF_FUNCTION(fps_proc);
51 // ==========================================================================
52 // KERNEL CLASS ITSELF BELOW THIS
53 // ==========================================================================
55 int CGameKernel::timer_installed_count = 0; // how many times we installed the timer; when this is 0, remove/install timers
57 // -------------------------------------
58 // Constructor
59 // -------------------------------------
60 CGameKernel::CGameKernel()
62 mtracer.add("CGameKernel::CGameKernel()");
64 game_over = false;
66 dbuffer = NULL; // doble buffer
68 backdropbmp = NULL; // background bitmap for current level
70 current_level_file_name[0] = '\0';
72 stats.reset(); // reset stats
75 // -------------------------------------
76 // Destructor
77 // -------------------------------------
78 CGameKernel::~CGameKernel()
80 mtracer.add("CGameKernel::~CGameKernel()");
82 this->shutdown();
85 // -------------------------------------------------------------------
86 // Initializes game (sets timers, loads data, etc)
87 // -------------------------------------------------------------------
88 void CGameKernel::init()
90 mtracer.add("CGameKernel::init()");
92 if (dbuffer == NULL)
93 dbuffer = create_bitmap(SCREEN_W, SCREEN_H); // create doble buffer bitmap
95 if (dbuffer == NULL)
96 raise_error("CGameKernel::init()\nERROR: unable to create doble buffer bitmap!\n");
98 clear_bitmap(screen);
100 textout_centre_ex(screen, font, "[ Please wait... loading... ]", SCREEN_W / 2, SCREEN_H / 2, makecol(255, 255, 255), makecol(0, 0, 64));
102 // Load general data for the game
103 char tmp_file_buf[2048];
105 if (game_map.load_tile_set_from_file(where_is_the_filename(tmp_file_buf, TILESET_FILE)) )
106 raise_error("CGameKernel::init()\nERROR!\nCan't load \"%s\" file!\n", TILESET_FILE); // load tileset
108 if (main_data_file.load_datafile(where_is_the_filename(tmp_file_buf, SPRITES_FILE)) )
109 raise_error("CGameKernel::init()\nERROR!\nUnable to load \"%s\" file!\n", SPRITES_FILE);
111 mtracer.add("\tGeneral data loaded OK");
113 // find the ball's sprite texture and mask
114 player_ball.spr = (BITMAP *)main_data_file.get_resource_dat("BALL");
116 if (player_ball.spr == NULL)
117 raise_error("CGameKernel::init()\nERROR! Can't find BALL sprite!\n");
119 player_ball.spr_shadow = (BITMAP *)main_data_file.get_resource_dat("BALL_MASK_SHADOW");
121 if (player_ball.spr_shadow == NULL)
122 raise_error("CGameKernel::init()\nERROR! Can't find BALL_MASK_SHADOW sprite!\n");
124 mtracer.add("\tBall data loaded OK");
126 // find GUI fonts
127 game_time_font = (FONT *)main_data_file.get_resource_dat("GAME_TIME_FONT");
129 game_score_font = (FONT *)main_data_file.get_resource_dat("GAME_SCORE_FONT");
131 game_messages_font = (FONT *)main_data_file.get_resource_dat("GAME_MESSAGES_FONT");
133 // set some stuff
134 game_map.timer_tick_rate = GKERNEL_FPSQ; // set timer ratio for map
136 // set the timers
137 if (timer_installed_count == 0)
139 LOCK_VARIABLE(speed_counter);
140 LOCK_FUNCTION((void *)increment_speed_counter);
142 LOCK_VARIABLE(fps);
143 LOCK_VARIABLE(frame_count);
144 LOCK_FUNCTION((void *)fps_proc);
146 // install the timer
147 if (install_int_ex(increment_speed_counter, BPS_TO_TIMER(GKERNEL_FPSQ)))
148 raise_error("CGameKernel::init() : Can't install timer at %d bps\n", GKERNEL_FPSQ);
150 if (install_int(fps_proc, 1000))
151 raise_error("CGameKernel::init() : Can't install timer to measure fps.");
153 mtracer.add("\tTimer installed for first time");
156 timer_installed_count++; // just to make sure that nobody else will remove our precious timer until we are ready too
158 mtracer.add("\ttimer_installed_count = %d", timer_installed_count);
160 // here start all the variables and all stuff
161 game_over = false;
162 frame_count = fps = 0;
164 current_level_file_name[0] = '\0';
167 // -------------------------------------------------------------------
168 // shuts down game (unsets timers, unloads data, etc)
169 // -------------------------------------------------------------------
170 void CGameKernel::shutdown()
172 mtracer.add("CGameKernel::shutdown()");
174 // remove timer(s), only if active
175 if (timer_installed_count > 0)
177 timer_installed_count--;
179 mtracer.add("\ttimer_installed_count = %d", timer_installed_count);
181 if (timer_installed_count == 0)
183 remove_int(increment_speed_counter);
184 remove_int(fps_proc);
185 timer_installed_count = 0;
187 mtracer.add("\tTimer totally removed");
191 // FREE THE RAM USED
192 game_map.free_memory();
194 main_data_file.nuke_datafile();
196 particle_manager.nuke_particle_list();
198 if (dbuffer != NULL)
200 destroy_bitmap(dbuffer);
201 dbuffer = NULL;
204 if (backdropbmp != NULL)
206 destroy_bitmap(backdropbmp);
207 backdropbmp = NULL;
210 current_level_file_name[0] = '\0';
212 music_loader.free_memory();
214 background_loader.free_memory();
217 // -------------------------------------------------------------------
218 // This is the main game loop
220 // **NOTICE**
221 // YOU **MUST** CALL IN THIS ORDER:
222 // gkernel.init();
223 // gkernel.load_level_file("level.map");
224 // gkernel.game_loop();
225 // gkernel.shutdown();
227 // Will return CBALL_IS_DEAD, or CBALL_EXIT_LEVEL (dead, or won level)
229 // **NOTE** THE SPECIAL RETURN VALUE 'GKERNEL_USER_FINISHED_GAME' MEANS THAT THE PLAYER
230 // DON'T WANT TO PLAY (HE HIT ESC, SO ABORT THE GAME)
231 // -------------------------------------------------------------------
232 int CGameKernel::game_loop()
234 int ret = 0;
235 mtracer.add("CGameKernel::game_loop()");
237 // Main game loop
238 game_over = false; // we start just now :P
240 speed_counter = 0;
242 // play music
243 soundw.music_start();
245 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)
247 while (speed_counter > 0)
249 ret = this->update_logic(); // update game logic
250 speed_counter--;
252 if (key[KEY_ESC])
254 game_over = true;
255 ret = GKERNEL_USER_FINISHED_GAME;
256 } // with ESC you end the game
258 if (ret == CBALL_IS_DEAD || ret == CBALL_EXIT_LEVEL)
259 game_over = true; // end of this game session
262 this->update_screen(); // update screen
264 frame_count++; // count fps
266 if (key[KEY_F12]) // with F12 you take a screenshoot
268 soundw.music_pause();
270 // watermark
271 textout_right_ex(screen, font, "(C) 2004, Kronoman", SCREEN_W, SCREEN_H - text_height(font), makecol(64, 64, 64),-1);
273 save_screenshoot("kball", 0, "bmp"); // save
275 // add confirmation message
277 ui_misc_text_out_shadow(screen, font, "Screenshoot saved", SCREEN_W / 2, SCREEN_H / 2, makecol(0, 0, 0), makecol(255, 255, 255));
278 clear_keybuf();
279 rest(800);
280 clear_keybuf();
281 this->update_screen();
282 speed_counter = 0;
284 soundw.music_resume();
287 if (key[KEY_ESC] || ret == GKERNEL_USER_FINISHED_GAME ) // with ESC you end the program
289 soundw.music_pause();
291 // add confirmation message
292 ui_misc_dark_bmp(screen);
293 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));
294 clear_keybuf();
295 rest(25);
296 clear_keybuf();
298 if ((readkey() >> 8) == KEY_Y)
300 game_over = true;
301 ret = GKERNEL_USER_FINISHED_GAME;
303 else
305 game_over = false;
306 ret = 0; // abort the exit -- DEBUG this, may be the misterious 0 value returned... :P
309 this->update_screen();
311 speed_counter = 0;
313 soundw.music_resume();
317 } // end of main game loop
320 soundw.music_stop();
322 return ret;
325 // -------------------------------------------------------------------
326 // this updates 1 logic update of game ; also has code to PAUSE the game (KEY P or PAUSE)
327 // will return CBALL_IS_DEAD, CBALL_IS_FINE, or CBALL_EXIT_LEVEL
328 // -------------------------------------------------------------------
329 int CGameKernel::update_logic()
331 int ret = CBALL_IS_FINE;
333 // statistics
335 // para contar el tiempo
336 static int last_t_s = 0;
337 if (++last_t_s > GKERNEL_FPSQ)
339 stats.add_time(0,0,1);
340 last_t_s = 0;
343 stats.score = player_ball.score;
344 // end statistics
346 if (key[KEY_PAUSE] || key[KEY_P]) // - pause the game -
348 soundw.music_pause();
350 ui_misc_dark_bmp(screen);
352 ui_misc_text_out_shadow(screen, game_messages_font, "- PAUSE -", SCREEN_W / 2, SCREEN_H / 2, makecol(0, 0, 0), makecol(0, 255, 255));
354 ui_misc_wait_for_input();
356 this->update_screen();
357 speed_counter = 0;
359 soundw.music_resume();
362 game_map.update_logic();
363 particle_manager.update_logic();
365 ret = player_ball.update_logic(); // return value
367 if (game_map.time_m*60 + game_map.time_s <= 10) // we are running low in time!, put a warning count
369 static int old_time = 0;
371 if (old_time != game_map.time_s) // only each second
373 old_time = game_map.time_s;
375 char tmp_str[80];
376 usprintf(tmp_str, "%d", game_map.time_s);
378 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));
382 if (game_map.time_m*60 + game_map.time_s <= 0) // time out, we are so DEAD!
384 ret = CBALL_IS_DEAD;
387 if (ret == CBALL_EXIT_LEVEL) // end of level :^D
389 if (game_map.prize_map_indispensable > 0) // we can't exit if we have prizes left.
391 // the user can't exit now, so drop a visual text saying it
392 if (rand() % 100 < 40)
393 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));
395 ret = CBALL_IS_FINE; // abort level exit
400 soundw.music_poll();
402 return ret;
405 // -------------------------------------------------------------------
406 // This updates the screen
407 // -------------------------------------------------------------------
408 void CGameKernel::update_screen()
410 int xc, yc; // x y of camera
411 static int blink_counter = 0; // counter for blink text timer... :P
412 static bool show_fps = false; // show fps ? F11 turns it
414 if (key[KEY_F11]) // show fps
415 show_fps = !show_fps;
417 blink_counter++;
419 if (blink_counter > GKERNEL_FPSQ)
420 blink_counter = 0;
422 // camera position
423 xc = (int)player_ball.x - SCREEN_W / 2;
425 yc = (int)player_ball.y - SCREEN_H / 2;
427 if (xc < 0)
428 xc = 0;
430 if (yc < 0)
431 yc = 0;
433 if (xc > TMAP_SIZE*TMAPS_W - SCREEN_W)
434 xc = TMAP_SIZE * TMAPS_W - SCREEN_W;
436 if (yc > TMAP_SIZE*TMAPS_H - SCREEN_H)
437 yc = TMAP_SIZE * TMAPS_H - SCREEN_H;
440 blit(backdropbmp, dbuffer, 0, 0, 0, 0, backdropbmp->w, backdropbmp->h); // blit background
442 //text_mode( -1); // transparent background for text
445 if (show_fps)
447 textprintf_ex(dbuffer, font, 0, SCREEN_H - text_height(font), makecol(255, 255, 255), -1, "%4d fps", fps);
448 textout_ex(dbuffer, font, "Copyright (c) 2003, 2004, 2005, Kronoman", 0, SCREEN_H - text_height(font)*3, makecol(128, 128, 255),-1);
451 // ---- 3D scene (this items are draw using the Allegro's 3D software polygon routines)
453 clear_scene(dbuffer); // clear 3D scene zbuffer
455 player_ball.draw(dbuffer, xc, yc); // draw ball
457 game_map.draw_map(dbuffer, xc, yc, SCREEN_W, SCREEN_H, 0, false); // draw map
459 game_map.draw_map(dbuffer, xc, yc, SCREEN_W, SCREEN_H, 1, false); // draw top layer (the one with prizes =D)
461 render_scene(); // render the 3D scene to the bitmap
464 // ---- end of 3D scene
466 // draw particles, they are 2D
467 particle_manager.render(dbuffer, xc, yc);
469 // time left message
470 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);
472 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);
474 // score message
475 textprintf_ex(dbuffer, game_score_font, 3, 3, makecol(128, 100, 0), -1,"%010ld", player_ball.score);
477 textprintf_ex(dbuffer, game_score_font, 0, 0, makecol(255, 200, 0), -1,"%010ld", player_ball.score);
479 // prizes left message
480 if (game_map.prize_map_indispensable > 0)
482 textprintf_right_ex(dbuffer, game_score_font, dbuffer->w + 3, 3, makecol(0, 64, 128),-1, "%04d", game_map.prize_map_indispensable);
483 textprintf_right_ex(dbuffer, game_score_font, dbuffer->w, 0, makecol(0, 128, 255),-1, "%04d", game_map.prize_map_indispensable);
485 else
487 int cc1, cc2; // text color for blink text
488 // the EXIT blinks... :D
489 if (blink_counter < GKERNEL_FPSQ / 2)
491 cc1 = makecol(0, 100, 128);
492 cc2 = makecol(0, 200, 255);
494 else
496 cc1 = makecol(128, 100, 0);
497 cc2 = makecol(255, 200, 0);
501 textprintf_right_ex(dbuffer, game_score_font, dbuffer->w + 3, 3, cc1,-1, "EXIT");
502 textprintf_right_ex(dbuffer, game_score_font, dbuffer->w, 0, cc2,-1, "EXIT");
505 // lives left message
506 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);
508 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);
510 // debug, remove this!
511 //textprintf(dbuffer, font,0,0,makecol(255,255,0), "x%3f,y%3f", player_ball.anglex,player_ball.angley);
514 //stats.print(dbuffer, 50, makecol(255,255,255), -1, font); // DEBUG -- REMOVE THIS OR MAKE IT TOGGEABLE WITH A KEY
516 blit(dbuffer, screen, 0, 0, 0, 0, dbuffer->w, dbuffer->h); // dump the render to screen
518 // yield_timeslice(); // play nice with multitask
519 //rest(0); // the way of playing nice with multitask since Allegro 4.1.15 WIP
522 // -------------------------------------------------------------------
523 // Helper function, takes a bitmap, and returns a new allocated bitmap
524 // with the 1st bitmap tiled in the size of the screen
525 // this serves for the background imagen
526 // -------------------------------------------------------------------
527 BITMAP *CGameKernel::tile_bmp_to_screen_size(BITMAP *bmp)
529 BITMAP *b = NULL;
531 if (bmp == NULL)
532 raise_error("CGameKernel::tile_bmp_to_screen_size()\nERROR: bitmap null; can't tile it!\n");
534 b = create_bitmap(SCREEN_W, SCREEN_H);
536 if (b == NULL)
537 raise_error("CGameKernel::tile_bmp_to_screen_size()\nERROR: unable to create bitmap!\n");
539 for (int y = 0; y < SCREEN_H + bmp->h; y += bmp->h)
540 for (int x = 0; x < SCREEN_W + bmp->w; x += bmp->w)
541 blit(bmp, b, 0, 0, x, y, bmp->w, bmp->h);
543 return b;
546 // -----------------------------------------------------------------------
547 // Loads a level from a file, and sets the game ready to play on that level
549 // *MUST* BE CALLED BEFORE STARTING THE GAME LOOP!
550 // WILL LOAD THE MAP, BACKGROUND, etc
551 // -----------------------------------------------------------------------
552 void CGameKernel::load_level_file(const char *file)
554 mtracer.add("CGameKernel::load_level_file(\"%s\")", file);
556 if (game_map.load_map_from_file(file))
557 raise_error("CGameKernel::load_level_file()\nERROR!\nCan't load level '%s'!\n", file);
559 usprintf(current_level_file_name, "%s", file); // save current file name
561 // set the player position and properties
562 player_ball.x = (game_map.sxp * TMAPS_W) + (TMAPS_W - BALL_RADIUS * 2) / 2;
564 player_ball.y = (game_map.syp * TMAPS_H) + (TMAPS_H - BALL_RADIUS * 2) / 2;
566 player_ball.z = 1.0;
568 player_ball.dx = player_ball.dy = player_ball.dz = 0.0;
570 player_ball.ctmap = &game_map;
572 player_ball.particle_manager = &particle_manager;
574 particle_manager.nuke_particle_list(); // CLEAN THE PARTICLES FOR THE NEW LEVEL :P
576 // DEBUG -- BACKGROUND! - DECIDIR QUE CORNO HAGO CON ESTO
577 // Load the background from info stored on the level map
578 if (backdropbmp != NULL)
580 destroy_bitmap(backdropbmp);
581 backdropbmp = NULL;
584 backdropbmp = tile_bmp_to_screen_size( background_loader.get_background("backgr.dat", game_map.background_index) );
586 mtracer.add("\tLoaded background (%d) of level OK", game_map.background_index);
588 // load music
589 soundw.music_load(music_loader.get_music("music_l.dat", game_map.music_index));
590 mtracer.add("\tLoaded music (%d) of level OK", game_map.music_index);
594 // -----------------------------------------------------------------------
595 // This will start a single level game
596 // Will return CBALL_IS_DEAD(totally DEAD, full game over, 0 lives)
597 // CBALL_EXIT_LEVEL (only useful for campaign), or GKERNEL_USER_FINISHED_GAME if player cancelled game
598 // -----------------------------------------------------------------------
599 int CGameKernel::play_a_single_level(char *level_filename)
601 int ret;
602 bool do_the_loop = true; // loop, for loading level many times, etc
603 int old_score = 0; // this is for restoring the score when the player loses a ball
605 mtracer.add("\nCGameKernel::play_a_single_level('%s');\n", level_filename);
607 this->init(); // init game kernel
609 old_score = player_ball.score;
611 while (do_the_loop)
613 mtracer.add("\tLoading and playing level.");
615 this->load_level_file(level_filename);
617 ret = this->game_loop();
619 mtracer.add("\t--> this->game_loop = %4d\n", ret);
621 // according to ret value, show the player, if we won, we lose, or if we have lives to keep playing, etc
622 // the { } in the case <x>: are there to force the scope, so don't remove them (I know what I'm doing...)
623 switch (ret)
626 case CBALL_EXIT_LEVEL:
628 // the player won the level, OK, so we exit
629 // this value will be used by campaign function, if available
631 this->update_screen();
633 ui_misc_dark_bmp(screen);
634 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));
636 // GIVE BONUS
637 int bonus = 0;
638 //text_mode(makecol(0, 0, 64));
640 clear_keybuf();
641 rest(550);
642 clear_keybuf();
644 rectfill(screen, 0, SCREEN_H / 2 + text_height(game_messages_font), SCREEN_W, SCREEN_H, makecol(0, 0, 64));
647 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));
649 for (int i = 0; i < game_map.time_m*60 + game_map.time_s; i++)
651 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);
652 bonus += 100; // 100 bonus points for each second
654 // DEBUG -- falta SONIDO!
656 if (keypressed() || mouse_b || joy[0].button[0].b)
657 break; // let the user cancel the count
659 poll_joystick();
661 if (keyboard_needs_poll())
662 poll_keyboard();
664 if (mouse_needs_poll())
665 poll_mouse();
667 rest(10);
670 // give real bonus
671 bonus = (game_map.time_m * 60 + game_map.time_s) * 100;
673 player_ball.score += bonus;
674 stats.score = player_ball.score;
676 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);
678 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);
680 //text_mode( -1);
682 textout_centre_ex(screen, font, "-- Hit any key to continue --", SCREEN_W / 2, SCREEN_H - text_height(font), makecol(255, 255, 255),-1);
684 ui_misc_wait_for_input();
686 do_the_loop = false;
689 break; // CBALL_EXIT_LEVEL
692 case CBALL_IS_DEAD:
694 this->update_screen();
696 ui_misc_dark_bmp(screen);
698 if (game_map.time_m*60 + game_map.time_s > 0)
699 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));
700 else
701 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));
703 textout_centre_ex(screen, font, "-- Hit any key to continue --", SCREEN_W / 2, SCREEN_H - text_height(font), makecol(255, 255, 255),-1);
705 ui_misc_wait_for_input();
707 // the player died, rest one live
708 player_ball.lives--;
709 stats.blost++; // statistics
710 stats.score = player_ball.score;
712 player_ball.anglex = player_ball.angley = 0.0; // reset ball rotations
714 if (player_ball.lives < 0) // the player is dead. 0 lives. GAME OVER. :(
717 BITMAP *bmp_game_over = (BITMAP *)main_data_file.get_resource_dat("GAME_OVER_BMP");
719 blit(bmp_game_over, screen, 0, 0, 0, 0, bmp_game_over->w, bmp_game_over->h);
721 stats.score = player_ball.score;
722 stats.print(screen, 150, makecol(255,255,0), -1, game_score_font); // print stats
724 // sound
725 SAMPLE *game_over_smp = (SAMPLE *)main_data_file.get_resource_dat("GAME_OVER_WAV");
726 play_sample(game_over_smp,255,128,1000,0);
728 textout_centre_ex(screen, font, "-- Hit any key to continue --", SCREEN_W / 2, SCREEN_H - text_height(font), makecol(128, 0, 0),-1);
730 ui_misc_wait_for_input();
732 do_the_loop = false;
734 stats.reset(); // reset stats
736 else
738 // perdio una vida, pero le queda, jugar de nuevo el nivel
739 do_the_loop = true; // load the next level, damn
740 player_ball.score = old_score; // restore score
745 break; // end of CBALL_IS_DEAD
747 case GKERNEL_USER_FINISHED_GAME:
748 do_the_loop = false; // the player hit <ESC>, end of game :(
749 break; // GKERNEL_USER_FINISHED_GAME
751 default: // it should NEVER come here
752 raise_error("ERROR at CGameKernel::play_a_single_level\nGuru meditation:\ngame_loop returned a invalid value (ret = %d)\n", ret);
753 break;
758 this->shutdown(); // shutdown this beauty...
760 return ret;
764 // -----------------------------------------------------------------------
765 // This will start a full campaign game
766 // -----------------------------------------------------------------------
767 void CGameKernel::play_a_full_campaign(char *level_filename)
769 char tmp_str[4096]; // buffer to handle level filenames
770 int current_level = 1; // current level number
771 bool finished = false;
772 int ret = 0;
774 mtracer.add("\nCGameKernel::play_a_full_campaign('%s')\n", level_filename);
776 while (!finished)
778 // load and play level
779 usprintf(tmp_str, "%s#%d_MAP", level_filename, current_level);
780 fix_filename_slashes(tmp_str);
782 // check if the level exits before trying to play it -- DEBUG -- slow code here! do some better
783 PACKFILE *fp_test = pack_fopen(tmp_str, F_READ);
785 if (fp_test == NULL)
787 // EL NIVEL NO EXISTE, LISTO, GANO
789 // the player won the campaign
791 // NOTA: dado que cuando llega aca, ya hizo el this->shutdown, los .DAT estan DESCARGADOS
792 // por eso, recargo el bitmap y font en RAM y el sonido...
794 clear_bitmap(screen);
795 textout_centre_ex(screen, font, "[ Please wait... loading... ]", SCREEN_W / 2, SCREEN_H / 2, makecol(255, 255, 255), makecol(0, 0, 64));
797 DATAFILE *dattmp = load_datafile_object(SPRITES_FILE, "WON_BMP");
798 if (!dattmp)
799 raise_error("CGameKernel::play_a_full_campaign\nCan't load %s -> WON_BMP\n", SPRITES_FILE);
800 BITMAP *bmp_won = (BITMAP *)dattmp->dat;
802 DATAFILE *dattmp2 = load_datafile_object(SPRITES_FILE, "GAME_SCORE_FONT");
803 if (!dattmp2)
804 raise_error("CGameKernel::play_a_full_campaign\nCan't load %s -> GAME_SCORE_FONT\n", SPRITES_FILE);
805 FONT *fs = (FONT *)dattmp2->dat;
808 blit(bmp_won, screen, 0, 0, 0, 0, bmp_won->w, bmp_won->h);
810 stats.print(screen, 150, makecol(0,0,255), -1, fs);
812 // sound
813 DATAFILE *dattmp3 = load_datafile_object(SPRITES_FILE, "WON_WAV");
814 if (dattmp3)
816 play_sample((SAMPLE *)dattmp3->dat,255,128,1000,0);
819 textout_centre_ex(screen, font, "-- Hit any key to continue --", SCREEN_W / 2, SCREEN_H - text_height(font), makecol(255, 255, 0),-1);
821 ui_misc_wait_for_input();
823 unload_datafile_object(dattmp); // bye bye bitmap... :)
824 unload_datafile_object(dattmp2); // bye bye font... :)
825 if (dattmp3)
826 unload_datafile_object(dattmp3); // bye bye sample
828 dattmp = NULL;
830 stats.reset();
832 return ; // chau... finalizo!
835 pack_fclose(fp_test);
837 ret = this->play_a_single_level(tmp_str);
839 mtracer.add("\t--> this->play_a_single_level('%s') = %4d\n", tmp_str, ret);
841 if (ret == CBALL_EXIT_LEVEL) // exit level!
843 current_level++;
844 finished = false;
846 else
848 finished = true; // any other value means GAME OVER, sorry d00d, insert coin :P