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);
191 st_pause_ticks_start();
196 GameSession::process_events()
198 if (end_sequence != NO_ENDSEQUENCE)
200 Player& tux = *world->get_tux();
204 tux.input.right = DOWN;
207 if (int(last_x_pos) == int(tux.base.x))
212 last_x_pos = tux.base.x;
215 while (SDL_PollEvent(&event))
217 /* Check for menu-events, if the menu is shown */
220 Menu::current()->event(event);
222 st_pause_ticks_stop();
227 case SDL_QUIT: /* Quit event - quit: */
228 st_abort("Received window close", "");
231 case SDL_KEYDOWN: /* A keypress! */
233 SDLKey key = event.key.keysym.sym;
237 case SDLK_ESCAPE: /* Escape: Open/Close the menu: */
245 case SDL_JOYBUTTONDOWN:
246 if (event.jbutton.button == joystick_keymap.start_button)
254 if(!Menu::current() && !game_pause)
255 st_pause_ticks_stop();
258 while (SDL_PollEvent(&event))
260 /* Check for menu-events, if the menu is shown */
263 Menu::current()->event(event);
265 st_pause_ticks_stop();
269 Player& tux = *world->get_tux();
273 case SDL_QUIT: /* Quit event - quit: */
274 st_abort("Received window close", "");
277 case SDL_KEYDOWN: /* A keypress! */
279 SDLKey key = event.key.keysym.sym;
281 if(tux.key_event(key,DOWN))
286 case SDLK_ESCAPE: /* Escape: Open/Close the menu: */
294 case SDL_KEYUP: /* A keyrelease! */
296 SDLKey key = event.key.keysym.sym;
298 if(tux.key_event(key, UP))
309 st_pause_ticks_stop();
314 st_pause_ticks_start();
321 tux.size = !tux.size;
324 tux.base.height = 64;
327 tux.base.height = 32;
332 player_status.distros += 50;
340 tux.invincible_timer.start(TUX_INVINCIBLE_TIME);
344 --player_status.lives;
348 player_status.score += 1000;
361 case SDL_JOYAXISMOTION:
362 if (event.jaxis.axis == joystick_keymap.x_axis)
364 if (event.jaxis.value < -joystick_keymap.dead_zone)
366 tux.input.left = DOWN;
367 tux.input.right = UP;
369 else if (event.jaxis.value > joystick_keymap.dead_zone)
372 tux.input.right = DOWN;
376 tux.input.left = DOWN;
377 tux.input.right = DOWN;
380 else if (event.jaxis.axis == joystick_keymap.y_axis)
382 if (event.jaxis.value > joystick_keymap.dead_zone)
383 tux.input.down = DOWN;
384 else if (event.jaxis.value < -joystick_keymap.dead_zone)
391 case SDL_JOYBUTTONDOWN:
392 if (event.jbutton.button == joystick_keymap.a_button)
394 else if (event.jbutton.button == joystick_keymap.b_button)
395 tux.input.fire = DOWN;
396 else if (event.jbutton.button == joystick_keymap.start_button)
399 case SDL_JOYBUTTONUP:
400 if (event.jbutton.button == joystick_keymap.a_button)
402 else if (event.jbutton.button == joystick_keymap.b_button)
415 GameSession::check_end_conditions()
417 Player* tux = world->get_tux();
420 int endpos = (World::current()->get_level()->width-5) * 32;
421 Tile* endtile = collision_goal(tux->base);
423 // fallback in case the other endpositions don't trigger
424 if (!end_sequence && tux->base.x >= endpos)
426 end_sequence = ENDSEQUENCE_WAITING;
428 music_manager->play_music(level_end_song, 0);
429 endsequence_timer.start(7000);
430 tux->invincible_timer.start(7000); //FIXME: Implement a winning timer for the end sequence (with special winning animation etc.)
432 else if(end_sequence && !endsequence_timer.check())
434 exit_status = LEVEL_FINISHED;
437 else if(end_sequence == ENDSEQUENCE_RUNNING && endtile && endtile->data >= 1)
439 end_sequence = ENDSEQUENCE_WAITING;
441 else if(!end_sequence && endtile && endtile->data == 0)
443 end_sequence = ENDSEQUENCE_RUNNING;
445 music_manager->play_music(level_end_song, 0);
446 endsequence_timer.start(7000); // 5 seconds until we finish the map
447 tux->invincible_timer.start(7000); //FIXME: Implement a winning timer for the end sequence (with special winning animation etc.)
449 else if (!end_sequence && tux->is_dead())
451 player_status.bonus = PlayerStatus::NO_BONUS;
453 if (player_status.lives < 0)
455 if(st_gl_mode != ST_GL_TEST)
458 exit_status = GAME_OVER;
461 { // Still has lives, so reset Tux to the levelstart
470 GameSession::action(double frame_ratio)
472 if (exit_status == NONE)
474 // Update Tux and the World
475 world->action(frame_ratio);
487 int x = screen->h / 20;
488 for(int i = 0; i < x; ++i)
490 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);
492 fillrect(0,0,screen->w,screen->h,rand() % 50, rand() % 50, rand() % 50, 128);
493 blue_text->drawf("PAUSE - Press 'P' To Play", 0, 230, A_HMIDDLE, A_TOP, 1);
498 Menu::current()->draw();
499 mouse_cursor->draw();
506 GameSession::process_menu()
508 Menu* menu = Menu::current();
513 if(menu == game_menu)
515 switch (game_menu->check())
518 st_pause_ticks_stop();
520 case MNID_ABORTLEVEL:
521 st_pause_ticks_stop();
522 exit_status = LEVEL_ABORT;
526 else if(menu == options_menu)
528 process_options_menu();
530 else if(menu == load_game_menu )
532 process_load_game_menu();
537 GameSession::ExitStatus
540 Menu::set_current(0);
545 update_time = last_update_time = st_get_ticks();
547 // Eat unneeded events
549 while (SDL_PollEvent(&event)) {}
553 while (exit_status == NONE)
555 /* Calculate the movement-factor */
556 double frame_ratio = ((double)(update_time-last_update_time))/((double)FRAME_RATE);
558 if(!frame_timer.check())
560 frame_timer.start(25);
561 ++global_frame_counter;
565 world->get_tux()->input.old_fire = world->get_tux()->input.fire;
570 // Update the world state and all objects in the world
571 // Do that with a constante time-delta so that the game will run
572 // determistic and not different on different machines
573 if(!game_pause && !Menu::current())
576 check_end_conditions();
577 if (end_sequence == ENDSEQUENCE_RUNNING)
578 action(frame_ratio/2);
579 else if(end_sequence == NO_ENDSEQUENCE)
590 /* Time stops in pause mode */
591 if(game_pause || Menu::current())
596 /* Set the time of the last update and the time of the current update */
597 last_update_time = update_time;
598 update_time = st_get_ticks();
600 /* Pause till next frame, if the machine running the game is too fast: */
601 /* FIXME: Works great for in OpenGl mode, where the CPU doesn't have to do that much. But
602 the results in SDL mode aren't perfect (thought the 100 FPS are reached), even on an AMD2500+. */
603 if(last_update_time >= update_time - 12)
606 update_time = st_get_ticks();
610 if (!time_left.check() && world->get_tux()->dying == DYING_NOT
612 world->get_tux()->kill(Player::KILL);
615 if(world->get_tux()->invincible_timer.check() && !end_sequence)
617 world->play_music(HERRING_MUSIC);
619 /* are we low on time ? */
620 else if (time_left.get_left() < TIME_WARNING && !end_sequence)
622 world->play_music(HURRYUP_MUSIC);
624 /* or just normal music? */
625 else if(world->get_music_type() != LEVEL_MUSIC && !end_sequence)
627 world->play_music(LEVEL_MUSIC);
630 /* Calculate frames per second */
634 fps_fps = (1000.0 / (float)fps_timer.get_gone()) * (float)fps_cnt;
636 if(!fps_timer.check())
638 fps_timer.start(1000);
647 /* Bounce a brick: */
648 void bumpbrick(float x, float y)
650 World::current()->add_bouncy_brick(((int)(x + 1) / 32) * 32,
653 play_sound(sounds[SND_BRICK], SOUND_CENTER_SPEAKER);
658 GameSession::drawstatus()
662 sprintf(str, "%d", player_status.score);
663 white_text->draw("SCORE", 0, 0, 1);
664 gold_text->draw(str, 96, 0, 1);
666 if(st_gl_mode == ST_GL_TEST)
668 white_text->draw("Press ESC To Return",0,20,1);
671 if(!time_left.check()) {
672 white_text->draw("TIME'S UP", 224, 0, 1);
673 } else if (time_left.get_left() > TIME_WARNING || (global_frame_counter % 10) < 5) {
674 sprintf(str, "%d", time_left.get_left() / 1000 );
675 white_text->draw("TIME", 224, 0, 1);
676 gold_text->draw(str, 304, 0, 1);
679 sprintf(str, "%d", player_status.distros);
680 white_text->draw("COINS", screen->h, 0, 1);
681 gold_text->draw(str, 608, 0, 1);
683 white_text->draw("LIVES", 480, 20);
684 if (player_status.lives >= 5)
686 sprintf(str, "%dx", player_status.lives);
687 gold_text->draw_align(str, 617, 20, A_RIGHT, A_TOP);
688 tux_life->draw(565+(18*3), 20);
692 for(int i= 0; i < player_status.lives; ++i)
693 tux_life->draw(565+(18*i),20);
698 sprintf(str, "%2.1f", fps_fps);
699 white_text->draw("FPS", screen->h, 40, 1);
700 gold_text->draw(str, screen->h + 60, 40, 1);
705 GameSession::drawendscreen()
709 if (get_level()->img_bkgd)
710 get_level()->img_bkgd->draw(0, 0);
712 drawgradient(get_level()->bkgd_top, get_level()->bkgd_bottom);
714 blue_text->drawf("GAMEOVER", 0, 200, A_HMIDDLE, A_TOP, 1);
716 sprintf(str, "SCORE: %d", player_status.score);
717 gold_text->drawf(str, 0, 224, A_HMIDDLE, A_TOP, 1);
719 sprintf(str, "COINS: %d", player_status.distros);
720 gold_text->drawf(str, 0, 256, A_HMIDDLE, A_TOP, 1);
725 wait_for_event(event,2000,5000,true);
729 GameSession::drawresultscreen(void)
733 if (get_level()->img_bkgd)
734 get_level()->img_bkgd->draw(0, 0);
736 drawgradient(get_level()->bkgd_top, get_level()->bkgd_bottom);
738 blue_text->drawf("Result:", 0, 200, A_HMIDDLE, A_TOP, 1);
740 sprintf(str, "SCORE: %d", player_status.score);
741 gold_text->drawf(str, 0, 224, A_HMIDDLE, A_TOP, 1);
743 sprintf(str, "COINS: %d", player_status.distros);
744 gold_text->drawf(str, 0, 256, A_HMIDDLE, A_TOP, 1);
749 wait_for_event(event,2000,5000,true);
752 std::string slotinfo(int slot)
757 sprintf(slotfile,"%s/slot%d.stsg",st_save_dir,slot);
759 lisp_object_t* savegame = lisp_read_from_file(slotfile);
762 LispReader reader(lisp_cdr(savegame));
763 reader.read_string("title", &title);
767 if (access(slotfile, F_OK) == 0)
770 snprintf(tmp,1024,"Slot %d - %s",slot, title.c_str());
772 snprintf(tmp, 1024,"Slot %d - Savegame",slot);
775 sprintf(tmp,"Slot %d - Free",slot);