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);
428 else if(end_sequence && !endsequence_timer.check())
430 exit_status = LEVEL_FINISHED;
433 else if(end_sequence == ENDSEQUENCE_RUNNING && endtile && endtile->data >= 1)
435 end_sequence = ENDSEQUENCE_WAITING;
437 else if(!end_sequence && endtile && endtile->data == 0)
439 end_sequence = ENDSEQUENCE_RUNNING;
441 music_manager->play_music(level_end_song, 0);
442 endsequence_timer.start(7000); // 5 seconds until we finish the map
444 else if (!end_sequence && tux->is_dead())
446 player_status.bonus = PlayerStatus::NO_BONUS;
447 player_status.lives -= 1;
449 if (player_status.lives < 0)
451 if(st_gl_mode != ST_GL_TEST)
454 exit_status = GAME_OVER;
457 { // Still has lives, so reset Tux to the levelstart
466 GameSession::action(double frame_ratio)
468 if (exit_status == NONE)
470 // Update Tux and the World
471 world->action(frame_ratio);
483 int x = screen->h / 20;
484 for(int i = 0; i < x; ++i)
486 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);
488 fillrect(0,0,screen->w,screen->h,rand() % 50, rand() % 50, rand() % 50, 128);
489 blue_text->drawf("PAUSE - Press 'P' To Play", 0, 230, A_HMIDDLE, A_TOP, 1);
494 Menu::current()->draw();
495 mouse_cursor->draw();
502 GameSession::process_menu()
504 Menu* menu = Menu::current();
509 if(menu == game_menu)
511 switch (game_menu->check())
514 st_pause_ticks_stop();
516 case MNID_ABORTLEVEL:
517 st_pause_ticks_stop();
518 exit_status = LEVEL_ABORT;
522 else if(menu == options_menu)
524 process_options_menu();
526 else if(menu == load_game_menu )
528 process_load_game_menu();
533 GameSession::ExitStatus
536 Menu::set_current(0);
541 update_time = last_update_time = st_get_ticks();
543 // Eat unneeded events
545 while (SDL_PollEvent(&event)) {}
549 float overlap = 0.0f;
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())
572 frame_ratio *= game_speed;
573 frame_ratio += overlap;
574 while (frame_ratio > 0)
577 check_end_conditions();
578 if (end_sequence == ENDSEQUENCE_RUNNING)
580 else if(end_sequence == NO_ENDSEQUENCE)
584 overlap = frame_ratio;
594 /* Time stops in pause mode */
595 if(game_pause || Menu::current())
600 /* Set the time of the last update and the time of the current update */
601 last_update_time = update_time;
602 update_time = st_get_ticks();
604 /* Pause till next frame, if the machine running the game is too fast: */
605 /* FIXME: Works great for in OpenGl mode, where the CPU doesn't have to do that much. But
606 the results in SDL mode aren't perfect (thought the 100 FPS are reached), even on an AMD2500+. */
607 if(last_update_time >= update_time - 12)
610 update_time = st_get_ticks();
614 if (!time_left.check() && world->get_tux()->dying == DYING_NOT
616 world->get_tux()->kill(Player::KILL);
619 if(world->get_tux()->invincible_timer.check() && !end_sequence)
621 world->play_music(HERRING_MUSIC);
623 /* are we low on time ? */
624 else if (time_left.get_left() < TIME_WARNING && !end_sequence)
626 world->play_music(HURRYUP_MUSIC);
628 /* or just normal music? */
629 else if(world->get_music_type() != LEVEL_MUSIC && !end_sequence)
631 world->play_music(LEVEL_MUSIC);
634 /* Calculate frames per second */
638 fps_fps = (1000.0 / (float)fps_timer.get_gone()) * (float)fps_cnt;
640 if(!fps_timer.check())
642 fps_timer.start(1000);
651 /* Bounce a brick: */
652 void bumpbrick(float x, float y)
654 World::current()->add_bouncy_brick(((int)(x + 1) / 32) * 32,
657 play_sound(sounds[SND_BRICK], SOUND_CENTER_SPEAKER);
662 GameSession::drawstatus()
666 sprintf(str, "%d", player_status.score);
667 white_text->draw("SCORE", 0, 0, 1);
668 gold_text->draw(str, 96, 0, 1);
670 if(st_gl_mode == ST_GL_TEST)
672 white_text->draw("Press ESC To Return",0,20,1);
676 if(!time_left.check()) {
677 white_text->draw("TIME'S UP", 224, 0, 1);
678 } else if (time_left.get_left() > TIME_WARNING || (global_frame_counter % 10) < 5) {
679 sprintf(str, "%d", time_left.get_left() / 1000 );
680 white_text->draw("TIME", 224, 0, 1);
681 gold_text->draw(str, 304, 0, 1);
685 sprintf(str, "%d", player_status.distros);
686 white_text->draw("COINS", screen->h, 0, 1);
687 gold_text->draw(str, 608, 0, 1);
689 white_text->draw("LIVES", 480, 20);
690 if (player_status.lives >= 5)
692 sprintf(str, "%dx", player_status.lives);
693 gold_text->draw_align(str, 617, 20, A_RIGHT, A_TOP);
694 tux_life->draw(565+(18*3), 20);
698 for(int i= 0; i < player_status.lives; ++i)
699 tux_life->draw(565+(18*i),20);
704 sprintf(str, "%2.1f", fps_fps);
705 white_text->draw("FPS", screen->h, 40, 1);
706 gold_text->draw(str, screen->h + 60, 40, 1);
711 GameSession::drawendscreen()
715 if (get_level()->img_bkgd)
716 get_level()->img_bkgd->draw(0, 0);
718 drawgradient(get_level()->bkgd_top, get_level()->bkgd_bottom);
720 blue_text->drawf("GAMEOVER", 0, 200, A_HMIDDLE, A_TOP, 1);
722 sprintf(str, "SCORE: %d", player_status.score);
723 gold_text->drawf(str, 0, 224, A_HMIDDLE, A_TOP, 1);
725 sprintf(str, "COINS: %d", player_status.distros);
726 gold_text->drawf(str, 0, 256, A_HMIDDLE, A_TOP, 1);
731 wait_for_event(event,2000,5000,true);
735 GameSession::drawresultscreen(void)
739 if (get_level()->img_bkgd)
740 get_level()->img_bkgd->draw(0, 0);
742 drawgradient(get_level()->bkgd_top, get_level()->bkgd_bottom);
744 blue_text->drawf("Result:", 0, 200, A_HMIDDLE, A_TOP, 1);
746 sprintf(str, "SCORE: %d", player_status.score);
747 gold_text->drawf(str, 0, 224, A_HMIDDLE, A_TOP, 1);
749 sprintf(str, "COINS: %d", player_status.distros);
750 gold_text->drawf(str, 0, 256, A_HMIDDLE, A_TOP, 1);
755 wait_for_event(event,2000,5000,true);
758 std::string slotinfo(int slot)
763 sprintf(slotfile,"%s/slot%d.stsg",st_save_dir,slot);
765 lisp_object_t* savegame = lisp_read_from_file(slotfile);
768 LispReader reader(lisp_cdr(savegame));
769 reader.read_string("title", &title);
773 if (access(slotfile, F_OK) == 0)
776 snprintf(tmp,1024,"Slot %d - %s",slot, title.c_str());
778 snprintf(tmp, 1024,"Slot %d - Savegame",slot);
781 sprintf(tmp,"Slot %d - Free",slot);