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();
267 /* Tell Tux that the keys are all down, otherwise
268 it could have nasty bugs, like going allways to the right
269 or whatever that key does */
270 Player& tux = *world->get_tux();
271 tux.key_event((SDLKey)keymap.jump, UP);
272 tux.key_event((SDLKey)keymap.duck, UP);
273 tux.key_event((SDLKey)keymap.left, UP);
274 tux.key_event((SDLKey)keymap.right, UP);
275 tux.key_event((SDLKey)keymap.fire, UP);
279 Player& tux = *world->get_tux();
283 case SDL_QUIT: /* Quit event - quit: */
284 st_abort("Received window close", "");
287 case SDL_KEYDOWN: /* A keypress! */
289 SDLKey key = event.key.keysym.sym;
291 if(tux.key_event(key,DOWN))
296 case SDLK_ESCAPE: /* Escape: Open/Close the menu: */
304 case SDL_KEYUP: /* A keyrelease! */
306 SDLKey key = event.key.keysym.sym;
308 if(tux.key_event(key, UP))
319 st_pause_ticks_stop();
324 st_pause_ticks_start();
331 tux.size = !tux.size;
334 tux.base.height = 64;
337 tux.base.height = 32;
342 player_status.distros += 50;
350 tux.invincible_timer.start(TUX_INVINCIBLE_TIME);
354 --player_status.lives;
358 player_status.score += 1000;
371 case SDL_JOYAXISMOTION:
372 if (event.jaxis.axis == joystick_keymap.x_axis)
374 if (event.jaxis.value < -joystick_keymap.dead_zone)
376 tux.input.left = DOWN;
377 tux.input.right = UP;
379 else if (event.jaxis.value > joystick_keymap.dead_zone)
382 tux.input.right = DOWN;
386 tux.input.left = DOWN;
387 tux.input.right = DOWN;
390 else if (event.jaxis.axis == joystick_keymap.y_axis)
392 if (event.jaxis.value > joystick_keymap.dead_zone)
393 tux.input.down = DOWN;
394 else if (event.jaxis.value < -joystick_keymap.dead_zone)
401 case SDL_JOYBUTTONDOWN:
402 if (event.jbutton.button == joystick_keymap.a_button)
404 else if (event.jbutton.button == joystick_keymap.b_button)
405 tux.input.fire = DOWN;
406 else if (event.jbutton.button == joystick_keymap.start_button)
409 case SDL_JOYBUTTONUP:
410 if (event.jbutton.button == joystick_keymap.a_button)
412 else if (event.jbutton.button == joystick_keymap.b_button)
425 GameSession::check_end_conditions()
427 Player* tux = world->get_tux();
430 int endpos = (World::current()->get_level()->width-5) * 32;
431 Tile* endtile = collision_goal(tux->base);
433 // fallback in case the other endpositions don't trigger
434 if (!end_sequence && tux->base.x >= endpos)
436 end_sequence = ENDSEQUENCE_WAITING;
438 music_manager->play_music(level_end_song, 0);
439 endsequence_timer.start(7000);
440 tux->invincible_timer.start(7000); //FIXME: Implement a winning timer for the end sequence (with special winning animation etc.)
442 else if(end_sequence && !endsequence_timer.check())
444 exit_status = LEVEL_FINISHED;
447 else if(end_sequence == ENDSEQUENCE_RUNNING && endtile && endtile->data >= 1)
449 end_sequence = ENDSEQUENCE_WAITING;
451 else if(!end_sequence && endtile && endtile->data == 0)
453 end_sequence = ENDSEQUENCE_RUNNING;
455 music_manager->play_music(level_end_song, 0);
456 endsequence_timer.start(7000); // 5 seconds until we finish the map
457 tux->invincible_timer.start(7000); //FIXME: Implement a winning timer for the end sequence (with special winning animation etc.)
459 else if (!end_sequence && tux->is_dead())
461 player_status.bonus = PlayerStatus::NO_BONUS;
463 if (player_status.lives < 0)
465 if(st_gl_mode != ST_GL_TEST)
468 exit_status = GAME_OVER;
471 { // Still has lives, so reset Tux to the levelstart
480 GameSession::action(double frame_ratio)
482 if (exit_status == NONE)
484 // Update Tux and the World
485 world->action(frame_ratio);
497 int x = screen->h / 20;
498 for(int i = 0; i < x; ++i)
500 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);
502 fillrect(0,0,screen->w,screen->h,rand() % 50, rand() % 50, rand() % 50, 128);
503 blue_text->drawf("PAUSE - Press 'P' To Play", 0, 230, A_HMIDDLE, A_TOP, 1);
508 Menu::current()->draw();
509 mouse_cursor->draw();
516 GameSession::process_menu()
518 Menu* menu = Menu::current();
523 if(menu == game_menu)
525 switch (game_menu->check())
528 st_pause_ticks_stop();
530 case MNID_ABORTLEVEL:
531 st_pause_ticks_stop();
532 exit_status = LEVEL_ABORT;
536 else if(menu == options_menu)
538 process_options_menu();
540 else if(menu == load_game_menu )
542 process_load_game_menu();
547 GameSession::ExitStatus
550 Menu::set_current(0);
555 update_time = last_update_time = st_get_ticks();
557 // Eat unneeded events
559 while (SDL_PollEvent(&event)) {}
563 while (exit_status == NONE)
565 /* Calculate the movement-factor */
566 double frame_ratio = ((double)(update_time-last_update_time))/((double)FRAME_RATE);
568 if(!frame_timer.check())
570 frame_timer.start(25);
571 ++global_frame_counter;
575 world->get_tux()->input.old_fire = world->get_tux()->input.fire;
580 // Update the world state and all objects in the world
581 // Do that with a constante time-delta so that the game will run
582 // determistic and not different on different machines
583 if(!game_pause && !Menu::current())
586 check_end_conditions();
587 if (end_sequence == ENDSEQUENCE_RUNNING)
588 action(frame_ratio/2);
589 else if(end_sequence == NO_ENDSEQUENCE)
600 /* Time stops in pause mode */
601 if(game_pause || Menu::current())
606 /* Set the time of the last update and the time of the current update */
607 last_update_time = update_time;
608 update_time = st_get_ticks();
610 /* Pause till next frame, if the machine running the game is too fast: */
611 /* FIXME: Works great for in OpenGl mode, where the CPU doesn't have to do that much. But
612 the results in SDL mode aren't perfect (thought the 100 FPS are reached), even on an AMD2500+. */
613 if(last_update_time >= update_time - 12)
616 update_time = st_get_ticks();
620 if (!time_left.check() && world->get_tux()->dying == DYING_NOT
622 world->get_tux()->kill(Player::KILL);
625 if(world->get_tux()->invincible_timer.check() && !end_sequence)
627 world->play_music(HERRING_MUSIC);
629 /* are we low on time ? */
630 else if (time_left.get_left() < TIME_WARNING && !end_sequence)
632 world->play_music(HURRYUP_MUSIC);
634 /* or just normal music? */
635 else if(world->get_music_type() != LEVEL_MUSIC && !end_sequence)
637 world->play_music(LEVEL_MUSIC);
640 /* Calculate frames per second */
644 fps_fps = (1000.0 / (float)fps_timer.get_gone()) * (float)fps_cnt;
646 if(!fps_timer.check())
648 fps_timer.start(1000);
657 /* Bounce a brick: */
658 void bumpbrick(float x, float y)
660 World::current()->add_bouncy_brick(((int)(x + 1) / 32) * 32,
663 play_sound(sounds[SND_BRICK], SOUND_CENTER_SPEAKER);
668 GameSession::drawstatus()
672 sprintf(str, "%d", player_status.score);
673 white_text->draw("SCORE", 0, 0, 1);
674 gold_text->draw(str, 96, 0, 1);
676 if(st_gl_mode == ST_GL_TEST)
678 white_text->draw("Press ESC To Return",0,20,1);
681 if(!time_left.check()) {
682 white_text->draw("TIME'S UP", 224, 0, 1);
683 } else if (time_left.get_left() > TIME_WARNING || (global_frame_counter % 10) < 5) {
684 sprintf(str, "%d", time_left.get_left() / 1000 );
685 white_text->draw("TIME", 224, 0, 1);
686 gold_text->draw(str, 304, 0, 1);
689 sprintf(str, "%d", player_status.distros);
690 white_text->draw("COINS", screen->h, 0, 1);
691 gold_text->draw(str, 608, 0, 1);
693 white_text->draw("LIVES", 480, 20);
694 if (player_status.lives >= 5)
696 sprintf(str, "%dx", player_status.lives);
697 gold_text->draw_align(str, 617, 20, A_RIGHT, A_TOP);
698 tux_life->draw(565+(18*3), 20);
702 for(int i= 0; i < player_status.lives; ++i)
703 tux_life->draw(565+(18*i),20);
708 sprintf(str, "%2.1f", fps_fps);
709 white_text->draw("FPS", screen->h, 40, 1);
710 gold_text->draw(str, screen->h + 60, 40, 1);
715 GameSession::drawendscreen()
719 if (get_level()->img_bkgd)
720 get_level()->img_bkgd->draw(0, 0);
722 drawgradient(get_level()->bkgd_top, get_level()->bkgd_bottom);
724 blue_text->drawf("GAMEOVER", 0, 200, A_HMIDDLE, A_TOP, 1);
726 sprintf(str, "SCORE: %d", player_status.score);
727 gold_text->drawf(str, 0, 224, A_HMIDDLE, A_TOP, 1);
729 sprintf(str, "COINS: %d", player_status.distros);
730 gold_text->drawf(str, 0, 256, A_HMIDDLE, A_TOP, 1);
735 wait_for_event(event,2000,5000,true);
739 GameSession::drawresultscreen(void)
743 if (get_level()->img_bkgd)
744 get_level()->img_bkgd->draw(0, 0);
746 drawgradient(get_level()->bkgd_top, get_level()->bkgd_bottom);
748 blue_text->drawf("Result:", 0, 200, A_HMIDDLE, A_TOP, 1);
750 sprintf(str, "SCORE: %d", player_status.score);
751 gold_text->drawf(str, 0, 224, A_HMIDDLE, A_TOP, 1);
753 sprintf(str, "COINS: %d", player_status.distros);
754 gold_text->drawf(str, 0, 256, A_HMIDDLE, A_TOP, 1);
759 wait_for_event(event,2000,5000,true);
762 std::string slotinfo(int slot)
767 sprintf(slotfile,"%s/slot%d.stsg",st_save_dir,slot);
769 lisp_object_t* savegame = lisp_read_from_file(slotfile);
772 LispReader reader(lisp_cdr(savegame));
773 reader.read_string("title", &title);
777 if (access(slotfile, F_OK) == 0)
780 snprintf(tmp,1024,"Slot %d - %s",slot, title.c_str());
782 snprintf(tmp, 1024,"Slot %d - Savegame",slot);
785 sprintf(tmp,"Slot %d - Free",slot);