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 "background.h"
57 #include "music_manager.h"
59 GameSession* GameSession::current_ = 0;
61 GameSession::GameSession(const std::string& subset_, int levelnb_, int mode)
62 : world(0), st_gl_mode(mode), levelnb(levelnb_), end_sequence(NO_ENDSEQUENCE),
67 global_frame_counter = 0;
72 frame_timer.init(true);
78 GameSession::restart_level()
81 exit_status = ES_NONE;
82 end_sequence = NO_ENDSEQUENCE;
85 frame_timer.init(true);
90 { // Tux has lost a life, so we try to respawn him at the nearest reset point
91 old_x_pos = world->get_tux()->base.x;
96 if (st_gl_mode == ST_GL_LOAD_LEVEL_FILE)
98 world = new World(subset);
100 else if (st_gl_mode == ST_GL_DEMO_GAME)
102 world = new World(subset);
106 world = new World(subset, levelnb);
109 // Set Tux to the nearest reset point
112 ResetPoint best_reset_point = { -1, -1 };
113 for(std::vector<ResetPoint>::iterator i = get_level()->reset_points.begin();
114 i != get_level()->reset_points.end(); ++i)
116 if (i->x < old_x_pos && best_reset_point.x < i->x)
117 best_reset_point = *i;
120 if (best_reset_point.x != -1)
122 world->get_tux()->base.x = best_reset_point.x;
123 world->get_tux()->base.y = best_reset_point.y;
127 if (st_gl_mode != ST_GL_DEMO_GAME)
129 if(st_gl_mode == ST_GL_PLAY || st_gl_mode == ST_GL_LOAD_LEVEL_FILE)
133 time_left.init(true);
135 world->play_music(LEVEL_MUSIC);
138 GameSession::~GameSession()
144 GameSession::levelintro(void)
146 music_manager->halt_music();
151 world->background->draw(dummy, LAYER_BACKGROUND0);
153 sprintf(str, "%s", world->get_level()->name.c_str());
154 gold_text->drawf(str, 0, 220, A_HMIDDLE, A_TOP, 1);
156 sprintf(str, "TUX x %d", player_status.lives);
157 white_text->drawf(str, 0, 240, A_HMIDDLE, A_TOP, 1);
159 sprintf(str, "by %s", world->get_level()->author.c_str());
160 white_small_text->drawf(str, 0, 400, 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()
181 if(world->get_tux()->dying || end_sequence != NO_ENDSEQUENCE)
182 return; // don't let the player open the menu, when he is dying
186 if(st_gl_mode == ST_GL_TEST)
188 exit_status = ES_LEVEL_ABORT;
190 else if (!Menu::current())
192 /* Tell Tux that the keys are all down, otherwise
193 it could have nasty bugs, like going allways to the right
194 or whatever that key does */
195 Player& tux = *world->get_tux();
196 tux.key_event((SDLKey)keymap.jump, UP);
197 tux.key_event((SDLKey)keymap.duck, UP);
198 tux.key_event((SDLKey)keymap.left, UP);
199 tux.key_event((SDLKey)keymap.right, UP);
200 tux.key_event((SDLKey)keymap.fire, UP);
202 Menu::set_current(game_menu);
203 st_pause_ticks_start();
208 GameSession::process_events()
210 if (end_sequence != NO_ENDSEQUENCE)
212 Player& tux = *world->get_tux();
216 tux.input.right = DOWN;
219 if (int(last_x_pos) == int(tux.base.x))
224 last_x_pos = tux.base.x;
227 while (SDL_PollEvent(&event))
229 /* Check for menu-events, if the menu is shown */
232 Menu::current()->event(event);
234 st_pause_ticks_stop();
239 case SDL_QUIT: /* Quit event - quit: */
240 st_abort("Received window close", "");
243 case SDL_KEYDOWN: /* A keypress! */
245 SDLKey key = event.key.keysym.sym;
249 case SDLK_ESCAPE: /* Escape: Open/Close the menu: */
257 case SDL_JOYBUTTONDOWN:
258 if (event.jbutton.button == joystick_keymap.start_button)
266 if(!Menu::current() && !game_pause)
267 st_pause_ticks_stop();
270 while (SDL_PollEvent(&event))
272 /* Check for menu-events, if the menu is shown */
275 Menu::current()->event(event);
277 st_pause_ticks_stop();
281 Player& tux = *world->get_tux();
285 case SDL_QUIT: /* Quit event - quit: */
286 st_abort("Received window close", "");
289 case SDL_KEYDOWN: /* A keypress! */
291 SDLKey key = event.key.keysym.sym;
293 if(tux.key_event(key,DOWN))
298 case SDLK_ESCAPE: /* Escape: Open/Close the menu: */
306 case SDL_KEYUP: /* A keyrelease! */
308 SDLKey key = event.key.keysym.sym;
310 if(tux.key_event(key, UP))
321 st_pause_ticks_stop();
326 st_pause_ticks_start();
333 tux.size = !tux.size;
336 tux.base.height = 64;
339 tux.base.height = 32;
344 player_status.distros += 50;
348 tux.got_power = tux.FIRE_POWER;
352 tux.got_power = tux.ICE_POWER;
356 tux.invincible_timer.start(TUX_INVINCIBLE_TIME);
360 --player_status.lives;
364 player_status.score += 1000;
377 case SDL_JOYAXISMOTION:
378 if (event.jaxis.axis == joystick_keymap.x_axis)
380 if (event.jaxis.value < -joystick_keymap.dead_zone)
382 tux.input.left = DOWN;
383 tux.input.right = UP;
385 else if (event.jaxis.value > joystick_keymap.dead_zone)
388 tux.input.right = DOWN;
392 tux.input.left = DOWN;
393 tux.input.right = DOWN;
396 else if (event.jaxis.axis == joystick_keymap.y_axis)
398 if (event.jaxis.value > joystick_keymap.dead_zone)
399 tux.input.down = DOWN;
400 else if (event.jaxis.value < -joystick_keymap.dead_zone)
407 case SDL_JOYBUTTONDOWN:
408 if (event.jbutton.button == joystick_keymap.a_button)
410 else if (event.jbutton.button == joystick_keymap.b_button)
411 tux.input.fire = DOWN;
412 else if (event.jbutton.button == joystick_keymap.start_button)
415 case SDL_JOYBUTTONUP:
416 if (event.jbutton.button == joystick_keymap.a_button)
418 else if (event.jbutton.button == joystick_keymap.b_button)
431 GameSession::check_end_conditions()
433 Player* tux = world->get_tux();
436 int endpos = (World::current()->get_level()->width-5) * 32;
437 Tile* endtile = collision_goal(tux->base);
439 // fallback in case the other endpositions don't trigger
440 if (!end_sequence && tux->base.x >= endpos)
442 end_sequence = ENDSEQUENCE_WAITING;
444 music_manager->play_music(level_end_song, 0);
445 endsequence_timer.start(7000);
446 tux->invincible_timer.start(7000); //FIXME: Implement a winning timer for the end sequence (with special winning animation etc.)
448 else if(end_sequence && !endsequence_timer.check())
450 exit_status = ES_LEVEL_FINISHED;
453 else if(end_sequence == ENDSEQUENCE_RUNNING && endtile && endtile->data >= 1)
455 end_sequence = ENDSEQUENCE_WAITING;
457 else if(!end_sequence && endtile && endtile->data == 0)
459 end_sequence = ENDSEQUENCE_RUNNING;
461 music_manager->play_music(level_end_song, 0);
462 endsequence_timer.start(7000); // 5 seconds until we finish the map
463 tux->invincible_timer.start(7000); //FIXME: Implement a winning timer for the end sequence (with special winning animation etc.)
465 else if (!end_sequence && tux->is_dead())
467 player_status.bonus = PlayerStatus::NO_BONUS;
469 if (player_status.lives < 0)
471 exit_status = ES_GAME_OVER;
474 { // Still has lives, so reset Tux to the levelstart
483 GameSession::action(double frame_ratio)
485 if (exit_status == ES_NONE)
487 // Update Tux and the World
488 world->action(frame_ratio);
500 int x = screen->h / 20;
501 for(int i = 0; i < x; ++i)
503 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);
505 fillrect(0,0,screen->w,screen->h,rand() % 50, rand() % 50, rand() % 50, 128);
506 blue_text->drawf("PAUSE - Press 'P' To Play", 0, 230, A_HMIDDLE, A_TOP, 1);
511 Menu::current()->draw();
512 mouse_cursor->draw();
519 GameSession::process_menu()
521 Menu* menu = Menu::current();
526 if(menu == game_menu)
528 switch (game_menu->check())
531 st_pause_ticks_stop();
533 case MNID_ABORTLEVEL:
534 st_pause_ticks_stop();
535 exit_status = ES_LEVEL_ABORT;
539 else if(menu == options_menu)
541 process_options_menu();
543 else if(menu == load_game_menu )
545 process_load_game_menu();
550 GameSession::ExitStatus
553 Menu::set_current(0);
558 update_time = last_update_time = st_get_ticks();
560 // Eat unneeded events
562 while (SDL_PollEvent(&event)) {}
566 while (exit_status == ES_NONE)
568 /* Calculate the movement-factor */
569 double frame_ratio = ((double)(update_time-last_update_time))/((double)FRAME_RATE);
571 if(!frame_timer.check())
573 frame_timer.start(25);
574 ++global_frame_counter;
578 world->get_tux()->input.old_fire = world->get_tux()->input.fire;
583 // Update the world state and all objects in the world
584 // Do that with a constante time-delta so that the game will run
585 // determistic and not different on different machines
586 if(!game_pause && !Menu::current())
589 check_end_conditions();
590 if (end_sequence == ENDSEQUENCE_RUNNING)
591 action(frame_ratio/2);
592 else if(end_sequence == NO_ENDSEQUENCE)
603 /* Time stops in pause mode */
604 if(game_pause || Menu::current())
609 /* Set the time of the last update and the time of the current update */
610 last_update_time = update_time;
611 update_time = st_get_ticks();
613 /* Pause till next frame, if the machine running the game is too fast: */
614 /* FIXME: Works great for in OpenGl mode, where the CPU doesn't have to do that much. But
615 the results in SDL mode aren't perfect (thought the 100 FPS are reached), even on an AMD2500+. */
616 if(last_update_time >= update_time - 12)
619 update_time = st_get_ticks();
623 if (!time_left.check() && world->get_tux()->dying == DYING_NOT
625 world->get_tux()->kill(Player::KILL);
628 if(world->get_tux()->invincible_timer.check() && !end_sequence)
630 world->play_music(HERRING_MUSIC);
632 /* are we low on time ? */
633 else if (time_left.get_left() < TIME_WARNING && !end_sequence)
635 world->play_music(HURRYUP_MUSIC);
637 /* or just normal music? */
638 else if(world->get_music_type() != LEVEL_MUSIC && !end_sequence)
640 world->play_music(LEVEL_MUSIC);
643 /* Calculate frames per second */
647 fps_fps = (1000.0 / (float)fps_timer.get_gone()) * (float)fps_cnt;
649 if(!fps_timer.check())
651 fps_timer.start(1000);
660 /* Bounce a brick: */
661 void bumpbrick(float x, float y)
663 World::current()->add_bouncy_brick(Vector(((int)(x + 1) / 32) * 32,
664 (int)(y / 32) * 32));
666 play_sound(sounds[SND_BRICK], SOUND_CENTER_SPEAKER);
671 GameSession::drawstatus()
675 snprintf(str, 60, "%d", player_status.score);
676 white_text->draw("SCORE", 0, 0, 1);
677 gold_text->draw(str, 96, 0, 1);
679 if(st_gl_mode == ST_GL_TEST)
681 white_text->draw("Press ESC To Return",0,20,1);
684 if(!time_left.check()) {
685 white_text->draw("TIME'S UP", screen->w/2 - white_text->w*8, 0, 1);
686 } else if (time_left.get_left() > TIME_WARNING || (global_frame_counter % 10) < 5) {
687 sprintf(str, "%d", time_left.get_left() / 1000 );
688 white_text->draw("TIME", screen->w/2 - white_text->w*4, 0, 1);
689 gold_text->draw(str, screen->w/2 + gold_text->w, 0, 1);
692 sprintf(str, "%d", player_status.distros);
693 white_text->draw("COINS", screen->w - white_text->w*9, 0, 1);
694 gold_text->draw(str, screen->w - gold_text->w*2, 0, 1);
696 white_text->draw("LIVES", screen->w - white_text->w*9, 20);
697 if (player_status.lives >= 5)
699 sprintf(str, "%dx", player_status.lives);
700 gold_text->draw_align(str, screen->w - gold_text->w, 20, A_RIGHT, A_TOP);
701 tux_life->draw(screen->w - gold_text->w, 20);
705 for(int i= 0; i < player_status.lives; ++i)
706 tux_life->draw(screen->w - tux_life->w*4 +(tux_life->w*i), 20);
711 sprintf(str, "%2.1f", fps_fps);
712 white_text->draw("FPS", screen->w - white_text->w*9, 40, 1);
713 gold_text->draw_align(str, screen->w, 40, A_RIGHT, A_TOP);
718 GameSession::drawresultscreen(void)
723 world->background->draw(dummy, LAYER_BACKGROUND0);
725 blue_text->drawf("Result:", 0, 200, A_HMIDDLE, A_TOP, 1);
727 sprintf(str, "SCORE: %d", player_status.score);
728 gold_text->drawf(str, 0, 224, A_HMIDDLE, A_TOP, 1);
730 sprintf(str, "COINS: %d", player_status.distros);
731 gold_text->drawf(str, 0, 256, A_HMIDDLE, A_TOP, 1);
736 wait_for_event(event,2000,5000,true);
739 std::string slotinfo(int slot)
744 sprintf(slotfile,"%s/slot%d.stsg",st_save_dir,slot);
746 lisp_object_t* savegame = lisp_read_from_file(slotfile);
749 LispReader reader(lisp_cdr(savegame));
750 reader.read_string("title", &title);
754 if (access(slotfile, F_OK) == 0)
757 snprintf(tmp,1024,"Slot %d - %s",slot, title.c_str());
759 snprintf(tmp, 1024,"Slot %d - Savegame",slot);
762 sprintf(tmp,"Slot %d - Free",slot);