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 while (exit_status == NONE)
551 /* Calculate the movement-factor */
552 double frame_ratio = ((double)(update_time-last_update_time))/((double)FRAME_RATE);
554 if(!frame_timer.check())
556 frame_timer.start(25);
557 ++global_frame_counter;
561 world->get_tux()->input.old_fire = world->get_tux()->input.fire;
566 // Update the world state and all objects in the world
567 // Do that with a constante time-delta so that the game will run
568 // determistic and not different on different machines
569 if(!game_pause && !Menu::current())
572 check_end_conditions();
573 if (end_sequence == ENDSEQUENCE_RUNNING)
574 action(frame_ratio/2);
575 else if(end_sequence == NO_ENDSEQUENCE)
586 /* Time stops in pause mode */
587 if(game_pause || Menu::current())
592 /* Set the time of the last update and the time of the current update */
593 last_update_time = update_time;
594 update_time = st_get_ticks();
596 /* Pause till next frame, if the machine running the game is too fast: */
597 /* FIXME: Works great for in OpenGl mode, where the CPU doesn't have to do that much. But
598 the results in SDL mode aren't perfect (thought the 100 FPS are reached), even on an AMD2500+. */
599 if(last_update_time >= update_time - 12)
602 update_time = st_get_ticks();
606 if (!time_left.check() && world->get_tux()->dying == DYING_NOT
608 world->get_tux()->kill(Player::KILL);
611 if(world->get_tux()->invincible_timer.check() && !end_sequence)
613 world->play_music(HERRING_MUSIC);
615 /* are we low on time ? */
616 else if (time_left.get_left() < TIME_WARNING && !end_sequence)
618 world->play_music(HURRYUP_MUSIC);
620 /* or just normal music? */
621 else if(world->get_music_type() != LEVEL_MUSIC && !end_sequence)
623 world->play_music(LEVEL_MUSIC);
626 /* Calculate frames per second */
630 fps_fps = (1000.0 / (float)fps_timer.get_gone()) * (float)fps_cnt;
632 if(!fps_timer.check())
634 fps_timer.start(1000);
643 /* Bounce a brick: */
644 void bumpbrick(float x, float y)
646 World::current()->add_bouncy_brick(((int)(x + 1) / 32) * 32,
649 play_sound(sounds[SND_BRICK], SOUND_CENTER_SPEAKER);
654 GameSession::drawstatus()
658 sprintf(str, "%d", player_status.score);
659 white_text->draw("SCORE", 0, 0, 1);
660 gold_text->draw(str, 96, 0, 1);
662 if(st_gl_mode == ST_GL_TEST)
664 white_text->draw("Press ESC To Return",0,20,1);
667 if(!time_left.check()) {
668 white_text->draw("TIME'S UP", 224, 0, 1);
669 } else if (time_left.get_left() > TIME_WARNING || (global_frame_counter % 10) < 5) {
670 sprintf(str, "%d", time_left.get_left() / 1000 );
671 white_text->draw("TIME", 224, 0, 1);
672 gold_text->draw(str, 304, 0, 1);
675 sprintf(str, "%d", player_status.distros);
676 white_text->draw("COINS", screen->h, 0, 1);
677 gold_text->draw(str, 608, 0, 1);
679 white_text->draw("LIVES", 480, 20);
680 if (player_status.lives >= 5)
682 sprintf(str, "%dx", player_status.lives);
683 gold_text->draw_align(str, 617, 20, A_RIGHT, A_TOP);
684 tux_life->draw(565+(18*3), 20);
688 for(int i= 0; i < player_status.lives; ++i)
689 tux_life->draw(565+(18*i),20);
694 sprintf(str, "%2.1f", fps_fps);
695 white_text->draw("FPS", screen->h, 40, 1);
696 gold_text->draw(str, screen->h + 60, 40, 1);
701 GameSession::drawendscreen()
705 if (get_level()->img_bkgd)
706 get_level()->img_bkgd->draw(0, 0);
708 drawgradient(get_level()->bkgd_top, get_level()->bkgd_bottom);
710 blue_text->drawf("GAMEOVER", 0, 200, A_HMIDDLE, A_TOP, 1);
712 sprintf(str, "SCORE: %d", player_status.score);
713 gold_text->drawf(str, 0, 224, A_HMIDDLE, A_TOP, 1);
715 sprintf(str, "COINS: %d", player_status.distros);
716 gold_text->drawf(str, 0, 256, A_HMIDDLE, A_TOP, 1);
721 wait_for_event(event,2000,5000,true);
725 GameSession::drawresultscreen(void)
729 if (get_level()->img_bkgd)
730 get_level()->img_bkgd->draw(0, 0);
732 drawgradient(get_level()->bkgd_top, get_level()->bkgd_bottom);
734 blue_text->drawf("Result:", 0, 200, A_HMIDDLE, A_TOP, 1);
736 sprintf(str, "SCORE: %d", player_status.score);
737 gold_text->drawf(str, 0, 224, A_HMIDDLE, A_TOP, 1);
739 sprintf(str, "COINS: %d", player_status.distros);
740 gold_text->drawf(str, 0, 256, A_HMIDDLE, A_TOP, 1);
745 wait_for_event(event,2000,5000,true);
748 std::string slotinfo(int slot)
753 sprintf(slotfile,"%s/slot%d.stsg",st_save_dir,slot);
755 lisp_object_t* savegame = lisp_read_from_file(slotfile);
758 LispReader reader(lisp_cdr(savegame));
759 reader.read_string("title", &title);
763 if (access(slotfile, F_OK) == 0)
766 snprintf(tmp,1024,"Slot %d - %s",slot, title.c_str());
768 snprintf(tmp, 1024,"Slot %d - Savegame",slot);
771 sprintf(tmp,"Slot %d - Free",slot);