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()
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()->img_bkgd->draw(0, 0);
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()
184 if(st_gl_mode == ST_GL_TEST)
186 exit_status = LEVEL_ABORT;
188 else if (!Menu::current())
190 Menu::set_current(game_menu);
195 GameSession::process_events()
197 if (end_sequence != NO_ENDSEQUENCE)
199 Player& tux = *world->get_tux();
203 tux.input.right = DOWN;
206 if (int(last_x_pos) == int(tux.base.x))
211 last_x_pos = tux.base.x;
214 while (SDL_PollEvent(&event))
216 /* Check for menu-events, if the menu is shown */
219 Menu::current()->event(event);
220 st_pause_ticks_start();
225 case SDL_QUIT: /* Quit event - quit: */
226 st_abort("Received window close", "");
229 case SDL_KEYDOWN: /* A keypress! */
231 SDLKey key = event.key.keysym.sym;
235 case SDLK_ESCAPE: /* Escape: Open/Close the menu: */
243 case SDL_JOYBUTTONDOWN:
244 if (event.jbutton.button == joystick_keymap.start_button)
252 if(!Menu::current() && !game_pause)
253 st_pause_ticks_stop();
256 while (SDL_PollEvent(&event))
258 /* Check for menu-events, if the menu is shown */
261 Menu::current()->event(event);
262 st_pause_ticks_start();
266 Player& tux = *world->get_tux();
270 case SDL_QUIT: /* Quit event - quit: */
271 st_abort("Received window close", "");
274 case SDL_KEYDOWN: /* A keypress! */
276 SDLKey key = event.key.keysym.sym;
278 if(tux.key_event(key,DOWN))
283 case SDLK_ESCAPE: /* Escape: Open/Close the menu: */
291 case SDL_KEYUP: /* A keyrelease! */
293 SDLKey key = event.key.keysym.sym;
295 if(tux.key_event(key, UP))
306 st_pause_ticks_stop();
311 st_pause_ticks_start();
318 tux.size = !tux.size;
321 tux.base.height = 64;
324 tux.base.height = 32;
329 player_status.distros += 50;
337 tux.invincible_timer.start(TUX_INVINCIBLE_TIME);
341 --player_status.lives;
345 player_status.score += 1000;
358 case SDL_JOYAXISMOTION:
359 if (event.jaxis.axis == joystick_keymap.x_axis)
361 if (event.jaxis.value < -joystick_keymap.dead_zone)
363 tux.input.left = DOWN;
364 tux.input.right = UP;
366 else if (event.jaxis.value > joystick_keymap.dead_zone)
369 tux.input.right = DOWN;
373 tux.input.left = DOWN;
374 tux.input.right = DOWN;
377 else if (event.jaxis.axis == joystick_keymap.y_axis)
379 if (event.jaxis.value > joystick_keymap.dead_zone)
380 tux.input.down = DOWN;
381 else if (event.jaxis.value < -joystick_keymap.dead_zone)
388 case SDL_JOYBUTTONDOWN:
389 if (event.jbutton.button == joystick_keymap.a_button)
391 else if (event.jbutton.button == joystick_keymap.b_button)
392 tux.input.fire = DOWN;
393 else if (event.jbutton.button == joystick_keymap.start_button)
396 case SDL_JOYBUTTONUP:
397 if (event.jbutton.button == joystick_keymap.a_button)
399 else if (event.jbutton.button == joystick_keymap.b_button)
412 GameSession::check_end_conditions()
414 Player* tux = world->get_tux();
417 int endpos = (World::current()->get_level()->width-5) * 32;
418 Tile* endtile = collision_goal(tux->base);
420 // fallback in case the other endpositions don't trigger
421 if (!end_sequence && tux->base.x >= endpos)
423 end_sequence = ENDSEQUENCE_WAITING;
425 music_manager->play_music(level_end_song, 0);
426 endsequence_timer.start(7000);
427 tux->invincible_timer.start(7000); //FIXME: Implement a winning timer for the end sequence (with special winning animation etc.)
429 else if(end_sequence && !endsequence_timer.check())
431 exit_status = LEVEL_FINISHED;
434 else if(end_sequence == ENDSEQUENCE_RUNNING && endtile && endtile->data >= 1)
436 end_sequence = ENDSEQUENCE_WAITING;
438 else if(!end_sequence && endtile && endtile->data == 0)
440 end_sequence = ENDSEQUENCE_RUNNING;
442 music_manager->play_music(level_end_song, 0);
443 endsequence_timer.start(7000); // 5 seconds until we finish the map
444 tux->invincible_timer.start(7000); //FIXME: Implement a winning timer for the end sequence (with special winning animation etc.)
446 else if (!end_sequence && tux->is_dead())
448 player_status.bonus = PlayerStatus::NO_BONUS;
450 if (player_status.lives < 0)
452 if(st_gl_mode != ST_GL_TEST)
455 exit_status = GAME_OVER;
458 { // Still has lives, so reset Tux to the levelstart
467 GameSession::action(double frame_ratio)
469 if (exit_status == NONE)
471 // Update Tux and the World
472 world->action(frame_ratio);
484 int x = screen->h / 20;
485 for(int i = 0; i < x; ++i)
487 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);
489 fillrect(0,0,screen->w,screen->h,rand() % 50, rand() % 50, rand() % 50, 128);
490 blue_text->drawf("PAUSE - Press 'P' To Play", 0, 230, A_HMIDDLE, A_TOP, 1);
495 Menu::current()->draw();
496 mouse_cursor->draw();
503 GameSession::process_menu()
505 Menu* menu = Menu::current();
510 if(menu == game_menu)
512 switch (game_menu->check())
515 st_pause_ticks_stop();
517 case MNID_ABORTLEVEL:
518 st_pause_ticks_stop();
519 exit_status = LEVEL_ABORT;
523 else if(menu == options_menu)
525 process_options_menu();
527 else if(menu == load_game_menu )
529 process_load_game_menu();
534 GameSession::ExitStatus
537 Menu::set_current(0);
542 update_time = last_update_time = st_get_ticks();
544 // Eat unneeded events
546 while (SDL_PollEvent(&event)) {}
550 while (exit_status == NONE)
552 /* Calculate the movement-factor */
553 double frame_ratio = ((double)(update_time-last_update_time))/((double)FRAME_RATE);
555 if(!frame_timer.check())
557 frame_timer.start(25);
558 ++global_frame_counter;
562 world->get_tux()->input.old_fire = world->get_tux()->input.fire;
567 // Update the world state and all objects in the world
568 // Do that with a constante time-delta so that the game will run
569 // determistic and not different on different machines
570 if(!game_pause && !Menu::current())
573 check_end_conditions();
574 if (end_sequence == ENDSEQUENCE_RUNNING)
575 action(frame_ratio/2);
576 else if(end_sequence == NO_ENDSEQUENCE)
587 /* Time stops in pause mode */
588 if(game_pause || Menu::current())
593 /* Set the time of the last update and the time of the current update */
594 last_update_time = update_time;
595 update_time = st_get_ticks();
597 /* Pause till next frame, if the machine running the game is too fast: */
598 /* FIXME: Works great for in OpenGl mode, where the CPU doesn't have to do that much. But
599 the results in SDL mode aren't perfect (thought the 100 FPS are reached), even on an AMD2500+. */
600 if(last_update_time >= update_time - 12)
603 update_time = st_get_ticks();
607 if (!time_left.check() && world->get_tux()->dying == DYING_NOT
609 world->get_tux()->kill(Player::KILL);
612 if(world->get_tux()->invincible_timer.check() && !end_sequence)
614 world->play_music(HERRING_MUSIC);
616 /* are we low on time ? */
617 else if (time_left.get_left() < TIME_WARNING && !end_sequence)
619 world->play_music(HURRYUP_MUSIC);
621 /* or just normal music? */
622 else if(world->get_music_type() != LEVEL_MUSIC && !end_sequence)
624 world->play_music(LEVEL_MUSIC);
627 /* Calculate frames per second */
631 fps_fps = (1000.0 / (float)fps_timer.get_gone()) * (float)fps_cnt;
633 if(!fps_timer.check())
635 fps_timer.start(1000);
644 /* Bounce a brick: */
645 void bumpbrick(float x, float y)
647 World::current()->add_bouncy_brick(((int)(x + 1) / 32) * 32,
650 play_sound(sounds[SND_BRICK], SOUND_CENTER_SPEAKER);
655 GameSession::drawstatus()
659 sprintf(str, "%d", player_status.score);
660 white_text->draw("SCORE", 0, 0, 1);
661 gold_text->draw(str, 96, 0, 1);
663 if(st_gl_mode == ST_GL_TEST)
665 white_text->draw("Press ESC To Return",0,20,1);
668 if(!time_left.check()) {
669 white_text->draw("TIME'S UP", 224, 0, 1);
670 } else if (time_left.get_left() > TIME_WARNING || (global_frame_counter % 10) < 5) {
671 sprintf(str, "%d", time_left.get_left() / 1000 );
672 white_text->draw("TIME", 224, 0, 1);
673 gold_text->draw(str, 304, 0, 1);
676 sprintf(str, "%d", player_status.distros);
677 white_text->draw("COINS", screen->h, 0, 1);
678 gold_text->draw(str, 608, 0, 1);
680 white_text->draw("LIVES", 480, 20);
681 if (player_status.lives >= 5)
683 sprintf(str, "%dx", player_status.lives);
684 gold_text->draw_align(str, 617, 20, A_RIGHT, A_TOP);
685 tux_life->draw(565+(18*3), 20);
689 for(int i= 0; i < player_status.lives; ++i)
690 tux_life->draw(565+(18*i),20);
695 sprintf(str, "%2.1f", fps_fps);
696 white_text->draw("FPS", screen->h, 40, 1);
697 gold_text->draw(str, screen->h + 60, 40, 1);
702 GameSession::drawendscreen()
706 if (get_level()->img_bkgd)
707 get_level()->img_bkgd->draw(0, 0);
709 drawgradient(get_level()->bkgd_top, get_level()->bkgd_bottom);
711 blue_text->drawf("GAMEOVER", 0, 200, A_HMIDDLE, A_TOP, 1);
713 sprintf(str, "SCORE: %d", player_status.score);
714 gold_text->drawf(str, 0, 224, A_HMIDDLE, A_TOP, 1);
716 sprintf(str, "COINS: %d", player_status.distros);
717 gold_text->drawf(str, 0, 256, A_HMIDDLE, A_TOP, 1);
722 wait_for_event(event,2000,5000,true);
726 GameSession::drawresultscreen(void)
730 if (get_level()->img_bkgd)
731 get_level()->img_bkgd->draw(0, 0);
733 drawgradient(get_level()->bkgd_top, get_level()->bkgd_bottom);
735 blue_text->drawf("Result:", 0, 200, A_HMIDDLE, A_TOP, 1);
737 sprintf(str, "SCORE: %d", player_status.score);
738 gold_text->drawf(str, 0, 224, A_HMIDDLE, A_TOP, 1);
740 sprintf(str, "COINS: %d", player_status.distros);
741 gold_text->drawf(str, 0, 256, A_HMIDDLE, A_TOP, 1);
746 wait_for_event(event,2000,5000,true);
749 std::string slotinfo(int slot)
754 sprintf(slotfile,"%s/slot%d.stsg",st_save_dir,slot);
756 lisp_object_t* savegame = lisp_read_from_file(slotfile);
759 LispReader reader(lisp_cdr(savegame));
760 reader.read_string("title", &title);
764 if (access(slotfile, F_OK) == 0)
767 snprintf(tmp,1024,"Slot %d - %s",slot, title.c_str());
769 snprintf(tmp, 1024,"Slot %d - Savegame",slot);
772 sprintf(tmp,"Slot %d - Free",slot);