4 // Copyright (C) 2000 Bill Kendrick <bill@newbreedsoftware.com>
5 // Copyright (C) 2004 Tobias Glaesser <tobi.web@gmx.de>
6 // Copyright (C) 2004 Ingo Ruhnke <grumbel@gmx.de>
8 // This program is free software; you can redistribute it and/or
9 // modify it under the terms of the GNU General Public License
10 // as published by the Free Software Foundation; either version 2
11 // of the License, or (at your option) any later version.
13 // This program is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 // GNU General Public License for more details.
18 // You should have received a copy of the GNU General Public License
19 // along with this program; if not, write to the Free Software
20 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
35 #include <sys/types.h>
44 #include "high_scores.h"
52 #include "collision.h"
54 #include "particlesystem.h"
55 #include "resources.h"
56 #include "music_manager.h"
58 GameSession* GameSession::current_ = 0;
60 GameSession::GameSession(const std::string& subset_, int levelnb_, int mode)
61 : world(0), st_gl_mode(mode), levelnb(levelnb_), end_sequence(NO_ENDSEQUENCE),
66 global_frame_counter = 0;
70 frame_timer.init(true);
76 GameSession::restart_level()
79 exit_status = ES_NONE;
80 end_sequence = NO_ENDSEQUENCE;
83 frame_timer.init(true);
88 { // Tux has lost a life, so we try to respawn him at the nearest reset point
89 old_x_pos = world->get_tux()->base.x;
94 if (st_gl_mode == ST_GL_LOAD_LEVEL_FILE)
96 world = new World(subset);
98 else if (st_gl_mode == ST_GL_DEMO_GAME)
100 world = new World(subset);
104 world = new World(subset, levelnb);
107 // Set Tux to the nearest reset point
110 ResetPoint best_reset_point = { -1, -1 };
111 for(std::vector<ResetPoint>::iterator i = get_level()->reset_points.begin();
112 i != get_level()->reset_points.end(); ++i)
114 if (i->x < old_x_pos && best_reset_point.x < i->x)
115 best_reset_point = *i;
118 if (best_reset_point.x != -1)
120 world->get_tux()->base.x = best_reset_point.x;
121 world->get_tux()->base.y = best_reset_point.y;
125 if (st_gl_mode != ST_GL_DEMO_GAME)
127 if(st_gl_mode == ST_GL_PLAY || st_gl_mode == ST_GL_LOAD_LEVEL_FILE)
131 time_left.init(true);
133 world->play_music(LEVEL_MUSIC);
136 GameSession::~GameSession()
142 GameSession::levelintro(void)
144 music_manager->halt_music();
148 if (get_level()->img_bkgd)
149 get_level()->draw_bg();
151 drawgradient(get_level()->bkgd_top, get_level()->bkgd_bottom);
153 sprintf(str, "%s", world->get_level()->name.c_str());
154 gold_text->drawf(str, 0, 200, A_HMIDDLE, A_TOP, 1);
156 sprintf(str, "TUX x %d", player_status.lives);
157 white_text->drawf(str, 0, 224, A_HMIDDLE, A_TOP, 1);
159 sprintf(str, "by %s", world->get_level()->author.c_str());
160 white_small_text->drawf(str, 0, 360, A_HMIDDLE, A_TOP, 1);
166 wait_for_event(event,1000,3000,true);
171 GameSession::start_timers()
173 time_left.start(world->get_level()->time_left*1000);
174 st_pause_ticks_init();
175 update_time = st_get_ticks();
179 GameSession::on_escape_press()
181 if(world->get_tux()->dying || end_sequence != NO_ENDSEQUENCE)
182 return; // don't let the player open the menu, when he is dying
186 if(st_gl_mode == ST_GL_TEST)
188 exit_status = ES_LEVEL_ABORT;
190 else if (!Menu::current())
192 Menu::set_current(game_menu);
193 st_pause_ticks_start();
198 GameSession::process_events()
200 if (end_sequence != NO_ENDSEQUENCE)
202 Player& tux = *world->get_tux();
206 tux.input.right = DOWN;
209 if (int(last_x_pos) == int(tux.base.x))
214 last_x_pos = tux.base.x;
217 while (SDL_PollEvent(&event))
219 /* Check for menu-events, if the menu is shown */
222 Menu::current()->event(event);
224 st_pause_ticks_stop();
229 case SDL_QUIT: /* Quit event - quit: */
230 st_abort("Received window close", "");
233 case SDL_KEYDOWN: /* A keypress! */
235 SDLKey key = event.key.keysym.sym;
239 case SDLK_ESCAPE: /* Escape: Open/Close the menu: */
247 case SDL_JOYBUTTONDOWN:
248 if (event.jbutton.button == joystick_keymap.start_button)
256 if(!Menu::current() && !game_pause)
257 st_pause_ticks_stop();
260 while (SDL_PollEvent(&event))
262 /* Check for menu-events, if the menu is shown */
265 Menu::current()->event(event);
267 st_pause_ticks_stop();
269 /* Tell Tux that the keys are all down, otherwise
270 it could have nasty bugs, like going allways to the right
271 or whatever that key does */
272 Player& tux = *world->get_tux();
273 tux.key_event((SDLKey)keymap.jump, UP);
274 tux.key_event((SDLKey)keymap.duck, UP);
275 tux.key_event((SDLKey)keymap.left, UP);
276 tux.key_event((SDLKey)keymap.right, UP);
277 tux.key_event((SDLKey)keymap.fire, UP);
281 Player& tux = *world->get_tux();
285 case SDL_QUIT: /* Quit event - quit: */
286 st_abort("Received window close", "");
289 case SDL_KEYDOWN: /* A keypress! */
291 SDLKey key = event.key.keysym.sym;
293 if(tux.key_event(key,DOWN))
298 case SDLK_ESCAPE: /* Escape: Open/Close the menu: */
306 case SDL_KEYUP: /* A keyrelease! */
308 SDLKey key = event.key.keysym.sym;
310 if(tux.key_event(key, UP))
321 st_pause_ticks_stop();
326 st_pause_ticks_start();
333 tux.size = !tux.size;
336 tux.base.height = 64;
339 tux.base.height = 32;
344 player_status.distros += 50;
348 tux.got_power = tux.FIRE_POWER;
352 tux.got_power = tux.ICE_POWER;
356 tux.invincible_timer.start(TUX_INVINCIBLE_TIME);
360 --player_status.lives;
364 player_status.score += 1000;
377 case SDL_JOYAXISMOTION:
378 if (event.jaxis.axis == joystick_keymap.x_axis)
380 if (event.jaxis.value < -joystick_keymap.dead_zone)
382 tux.input.left = DOWN;
383 tux.input.right = UP;
385 else if (event.jaxis.value > joystick_keymap.dead_zone)
388 tux.input.right = DOWN;
392 tux.input.left = DOWN;
393 tux.input.right = DOWN;
396 else if (event.jaxis.axis == joystick_keymap.y_axis)
398 if (event.jaxis.value > joystick_keymap.dead_zone)
399 tux.input.down = DOWN;
400 else if (event.jaxis.value < -joystick_keymap.dead_zone)
407 case SDL_JOYBUTTONDOWN:
408 if (event.jbutton.button == joystick_keymap.a_button)
410 else if (event.jbutton.button == joystick_keymap.b_button)
411 tux.input.fire = DOWN;
412 else if (event.jbutton.button == joystick_keymap.start_button)
415 case SDL_JOYBUTTONUP:
416 if (event.jbutton.button == joystick_keymap.a_button)
418 else if (event.jbutton.button == joystick_keymap.b_button)
431 GameSession::check_end_conditions()
433 Player* tux = world->get_tux();
436 int endpos = (World::current()->get_level()->width-5) * 32;
437 Tile* endtile = collision_goal(tux->base);
439 // fallback in case the other endpositions don't trigger
440 if (!end_sequence && tux->base.x >= endpos)
442 end_sequence = ENDSEQUENCE_WAITING;
444 music_manager->play_music(level_end_song, 0);
445 endsequence_timer.start(7000);
446 tux->invincible_timer.start(7000); //FIXME: Implement a winning timer for the end sequence (with special winning animation etc.)
448 else if(end_sequence && !endsequence_timer.check())
450 exit_status = ES_LEVEL_FINISHED;
453 else if(end_sequence == ENDSEQUENCE_RUNNING && endtile && endtile->data >= 1)
455 end_sequence = ENDSEQUENCE_WAITING;
457 else if(!end_sequence && endtile && endtile->data == 0)
459 end_sequence = ENDSEQUENCE_RUNNING;
461 music_manager->play_music(level_end_song, 0);
462 endsequence_timer.start(7000); // 5 seconds until we finish the map
463 tux->invincible_timer.start(7000); //FIXME: Implement a winning timer for the end sequence (with special winning animation etc.)
465 else if (!end_sequence && tux->is_dead())
467 player_status.bonus = PlayerStatus::NO_BONUS;
469 if (player_status.lives < 0)
471 exit_status = ES_GAME_OVER;
474 { // Still has lives, so reset Tux to the levelstart
483 GameSession::action(double frame_ratio)
485 if (exit_status == ES_NONE)
487 // Update Tux and the World
488 world->action(frame_ratio);
500 int x = screen->h / 20;
501 for(int i = 0; i < x; ++i)
503 fillrect(i % 2 ? (pause_menu_frame * i)%screen->w : -((pause_menu_frame * i)%screen->w) ,(i*20+pause_menu_frame)%screen->h,screen->w,10,20,20,20, rand() % 20 + 1);
505 fillrect(0,0,screen->w,screen->h,rand() % 50, rand() % 50, rand() % 50, 128);
506 blue_text->drawf("PAUSE - Press 'P' To Play", 0, 230, A_HMIDDLE, A_TOP, 1);
511 Menu::current()->draw();
512 mouse_cursor->draw();
519 GameSession::process_menu()
521 Menu* menu = Menu::current();
526 if(menu == game_menu)
528 switch (game_menu->check())
531 st_pause_ticks_stop();
533 case MNID_ABORTLEVEL:
534 st_pause_ticks_stop();
535 exit_status = ES_LEVEL_ABORT;
539 else if(menu == options_menu)
541 process_options_menu();
543 else if(menu == load_game_menu )
545 process_load_game_menu();
550 GameSession::ExitStatus
553 Menu::set_current(0);
558 update_time = last_update_time = st_get_ticks();
560 // Eat unneeded events
562 while (SDL_PollEvent(&event)) {}
566 while (exit_status == ES_NONE)
568 /* Calculate the movement-factor */
569 double frame_ratio = ((double)(update_time-last_update_time))/((double)FRAME_RATE);
571 if(!frame_timer.check())
573 frame_timer.start(25);
574 ++global_frame_counter;
578 world->get_tux()->input.old_fire = world->get_tux()->input.fire;
583 // Update the world state and all objects in the world
584 // Do that with a constante time-delta so that the game will run
585 // determistic and not different on different machines
586 if(!game_pause && !Menu::current())
589 check_end_conditions();
590 if (end_sequence == ENDSEQUENCE_RUNNING)
591 action(frame_ratio/2);
592 else if(end_sequence == NO_ENDSEQUENCE)
603 /* Time stops in pause mode */
604 if(game_pause || Menu::current())
609 /* Set the time of the last update and the time of the current update */
610 last_update_time = update_time;
611 update_time = st_get_ticks();
613 /* Pause till next frame, if the machine running the game is too fast: */
614 /* FIXME: Works great for in OpenGl mode, where the CPU doesn't have to do that much. But
615 the results in SDL mode aren't perfect (thought the 100 FPS are reached), even on an AMD2500+. */
616 if(last_update_time >= update_time - 12)
619 update_time = st_get_ticks();
623 if (!time_left.check() && world->get_tux()->dying == DYING_NOT
625 world->get_tux()->kill(Player::KILL);
628 if(world->get_tux()->invincible_timer.check() && !end_sequence)
630 world->play_music(HERRING_MUSIC);
632 /* are we low on time ? */
633 else if (time_left.get_left() < TIME_WARNING && !end_sequence)
635 world->play_music(HURRYUP_MUSIC);
637 /* or just normal music? */
638 else if(world->get_music_type() != LEVEL_MUSIC && !end_sequence)
640 world->play_music(LEVEL_MUSIC);
643 /* Calculate frames per second */
647 fps_fps = (1000.0 / (float)fps_timer.get_gone()) * (float)fps_cnt;
649 if(!fps_timer.check())
651 fps_timer.start(1000);
660 /* Bounce a brick: */
661 void bumpbrick(float x, float y)
663 World::current()->add_bouncy_brick(((int)(x + 1) / 32) * 32,
666 play_sound(sounds[SND_BRICK], SOUND_CENTER_SPEAKER);
671 GameSession::drawstatus()
675 sprintf(str, "%d", player_status.score);
676 white_text->draw("SCORE", 0, 0, 1);
677 gold_text->draw(str, 96, 0, 1);
679 if(st_gl_mode == ST_GL_TEST)
681 white_text->draw("Press ESC To Return",0,20,1);
684 if(!time_left.check()) {
685 white_text->draw("TIME'S UP", screen->w/2 - white_text->w*8, 0, 1);
686 } else if (time_left.get_left() > TIME_WARNING || (global_frame_counter % 10) < 5) {
687 sprintf(str, "%d", time_left.get_left() / 1000 );
688 white_text->draw("TIME", screen->w/2 - white_text->w*4, 0, 1);
689 gold_text->draw(str, screen->w/2 + gold_text->w, 0, 1);
692 sprintf(str, "%d", player_status.distros);
693 white_text->draw("COINS", screen->w - white_text->w*9, 0, 1);
694 gold_text->draw(str, screen->w - gold_text->w*2, 0, 1);
696 white_text->draw("LIVES", screen->w - white_text->w*9, 20);
697 if (player_status.lives >= 5)
699 sprintf(str, "%dx", player_status.lives);
700 gold_text->draw_align(str, screen->w - gold_text->w, 20, A_RIGHT, A_TOP);
701 tux_life->draw(screen->w - gold_text->w, 20);
705 for(int i= 0; i < player_status.lives; ++i)
706 tux_life->draw(screen->w - tux_life->w*4 +(tux_life->w*i), 20);
711 sprintf(str, "%2.1f", fps_fps);
712 white_text->draw("FPS", screen->w - white_text->w*9, 40, 1);
713 gold_text->draw_align(str, screen->w, 40, A_RIGHT, A_TOP);
718 GameSession::drawresultscreen(void)
722 if (get_level()->img_bkgd)
723 get_level()->draw_bg();
725 drawgradient(get_level()->bkgd_top, get_level()->bkgd_bottom);
727 blue_text->drawf("Result:", 0, 200, A_HMIDDLE, A_TOP, 1);
729 sprintf(str, "SCORE: %d", player_status.score);
730 gold_text->drawf(str, 0, 224, A_HMIDDLE, A_TOP, 1);
732 sprintf(str, "COINS: %d", player_status.distros);
733 gold_text->drawf(str, 0, 256, A_HMIDDLE, A_TOP, 1);
738 wait_for_event(event,2000,5000,true);
741 std::string slotinfo(int slot)
746 sprintf(slotfile,"%s/slot%d.stsg",st_save_dir,slot);
748 lisp_object_t* savegame = lisp_read_from_file(slotfile);
751 LispReader reader(lisp_cdr(savegame));
752 reader.read_string("title", &title);
756 if (access(slotfile, F_OK) == 0)
759 snprintf(tmp,1024,"Slot %d - %s",slot, title.c_str());
761 snprintf(tmp, 1024,"Slot %d - Savegame",slot);
764 sprintf(tmp,"Slot %d - Free",slot);