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()
79 exit_status = ES_NONE;
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 = ES_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;
346 tux.got_power = tux.FIRE_POWER;
350 tux.got_power = tux.ICE_POWER;
354 tux.invincible_timer.start(TUX_INVINCIBLE_TIME);
358 --player_status.lives;
362 player_status.score += 1000;
375 case SDL_JOYAXISMOTION:
376 if (event.jaxis.axis == joystick_keymap.x_axis)
378 if (event.jaxis.value < -joystick_keymap.dead_zone)
380 tux.input.left = DOWN;
381 tux.input.right = UP;
383 else if (event.jaxis.value > joystick_keymap.dead_zone)
386 tux.input.right = DOWN;
390 tux.input.left = DOWN;
391 tux.input.right = DOWN;
394 else if (event.jaxis.axis == joystick_keymap.y_axis)
396 if (event.jaxis.value > joystick_keymap.dead_zone)
397 tux.input.down = DOWN;
398 else if (event.jaxis.value < -joystick_keymap.dead_zone)
405 case SDL_JOYBUTTONDOWN:
406 if (event.jbutton.button == joystick_keymap.a_button)
408 else if (event.jbutton.button == joystick_keymap.b_button)
409 tux.input.fire = DOWN;
410 else if (event.jbutton.button == joystick_keymap.start_button)
413 case SDL_JOYBUTTONUP:
414 if (event.jbutton.button == joystick_keymap.a_button)
416 else if (event.jbutton.button == joystick_keymap.b_button)
429 GameSession::check_end_conditions()
431 Player* tux = world->get_tux();
434 int endpos = (World::current()->get_level()->width-5) * 32;
435 Tile* endtile = collision_goal(tux->base);
437 // fallback in case the other endpositions don't trigger
438 if (!end_sequence && tux->base.x >= endpos)
440 end_sequence = ENDSEQUENCE_WAITING;
442 music_manager->play_music(level_end_song, 0);
443 endsequence_timer.start(7000);
444 tux->invincible_timer.start(7000); //FIXME: Implement a winning timer for the end sequence (with special winning animation etc.)
446 else if(end_sequence && !endsequence_timer.check())
448 exit_status = ES_LEVEL_FINISHED;
451 else if(end_sequence == ENDSEQUENCE_RUNNING && endtile && endtile->data >= 1)
453 end_sequence = ENDSEQUENCE_WAITING;
455 else if(!end_sequence && endtile && endtile->data == 0)
457 end_sequence = ENDSEQUENCE_RUNNING;
459 music_manager->play_music(level_end_song, 0);
460 endsequence_timer.start(7000); // 5 seconds until we finish the map
461 tux->invincible_timer.start(7000); //FIXME: Implement a winning timer for the end sequence (with special winning animation etc.)
463 else if (!end_sequence && tux->is_dead())
465 player_status.bonus = PlayerStatus::NO_BONUS;
467 if (player_status.lives < 0)
469 if(st_gl_mode != ST_GL_TEST)
472 exit_status = ES_GAME_OVER;
475 { // Still has lives, so reset Tux to the levelstart
484 GameSession::action(double frame_ratio)
486 if (exit_status == ES_NONE)
488 // Update Tux and the World
489 world->action(frame_ratio);
501 int x = screen->h / 20;
502 for(int i = 0; i < x; ++i)
504 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);
506 fillrect(0,0,screen->w,screen->h,rand() % 50, rand() % 50, rand() % 50, 128);
507 blue_text->drawf("PAUSE - Press 'P' To Play", 0, 230, A_HMIDDLE, A_TOP, 1);
512 Menu::current()->draw();
513 mouse_cursor->draw();
520 GameSession::process_menu()
522 Menu* menu = Menu::current();
527 if(menu == game_menu)
529 switch (game_menu->check())
532 st_pause_ticks_stop();
534 case MNID_ABORTLEVEL:
535 st_pause_ticks_stop();
536 exit_status = ES_LEVEL_ABORT;
540 else if(menu == options_menu)
542 process_options_menu();
544 else if(menu == load_game_menu )
546 process_load_game_menu();
551 GameSession::ExitStatus
554 Menu::set_current(0);
559 update_time = last_update_time = st_get_ticks();
561 // Eat unneeded events
563 while (SDL_PollEvent(&event)) {}
567 while (exit_status == ES_NONE)
569 /* Calculate the movement-factor */
570 double frame_ratio = ((double)(update_time-last_update_time))/((double)FRAME_RATE);
572 if(!frame_timer.check())
574 frame_timer.start(25);
575 ++global_frame_counter;
579 world->get_tux()->input.old_fire = world->get_tux()->input.fire;
584 // Update the world state and all objects in the world
585 // Do that with a constante time-delta so that the game will run
586 // determistic and not different on different machines
587 if(!game_pause && !Menu::current())
590 check_end_conditions();
591 if (end_sequence == ENDSEQUENCE_RUNNING)
592 action(frame_ratio/2);
593 else if(end_sequence == NO_ENDSEQUENCE)
604 /* Time stops in pause mode */
605 if(game_pause || Menu::current())
610 /* Set the time of the last update and the time of the current update */
611 last_update_time = update_time;
612 update_time = st_get_ticks();
614 /* Pause till next frame, if the machine running the game is too fast: */
615 /* FIXME: Works great for in OpenGl mode, where the CPU doesn't have to do that much. But
616 the results in SDL mode aren't perfect (thought the 100 FPS are reached), even on an AMD2500+. */
617 if(last_update_time >= update_time - 12)
620 update_time = st_get_ticks();
624 if (!time_left.check() && world->get_tux()->dying == DYING_NOT
626 world->get_tux()->kill(Player::KILL);
629 if(world->get_tux()->invincible_timer.check() && !end_sequence)
631 world->play_music(HERRING_MUSIC);
633 /* are we low on time ? */
634 else if (time_left.get_left() < TIME_WARNING && !end_sequence)
636 world->play_music(HURRYUP_MUSIC);
638 /* or just normal music? */
639 else if(world->get_music_type() != LEVEL_MUSIC && !end_sequence)
641 world->play_music(LEVEL_MUSIC);
644 /* Calculate frames per second */
648 fps_fps = (1000.0 / (float)fps_timer.get_gone()) * (float)fps_cnt;
650 if(!fps_timer.check())
652 fps_timer.start(1000);
661 /* Bounce a brick: */
662 void bumpbrick(float x, float y)
664 World::current()->add_bouncy_brick(((int)(x + 1) / 32) * 32,
667 play_sound(sounds[SND_BRICK], SOUND_CENTER_SPEAKER);
672 GameSession::drawstatus()
676 sprintf(str, "%d", player_status.score);
677 white_text->draw("SCORE", 0, 0, 1);
678 gold_text->draw(str, 96, 0, 1);
680 if(st_gl_mode == ST_GL_TEST)
682 white_text->draw("Press ESC To Return",0,20,1);
685 if(!time_left.check()) {
686 white_text->draw("TIME'S UP", 224, 0, 1);
687 } else if (time_left.get_left() > TIME_WARNING || (global_frame_counter % 10) < 5) {
688 sprintf(str, "%d", time_left.get_left() / 1000 );
689 white_text->draw("TIME", 224, 0, 1);
690 gold_text->draw(str, 304, 0, 1);
693 sprintf(str, "%d", player_status.distros);
694 white_text->draw("COINS", screen->h, 0, 1);
695 gold_text->draw(str, 608, 0, 1);
697 white_text->draw("LIVES", 480, 20);
698 if (player_status.lives >= 5)
700 sprintf(str, "%dx", player_status.lives);
701 gold_text->draw_align(str, 617, 20, A_RIGHT, A_TOP);
702 tux_life->draw(565+(18*3), 20);
706 for(int i= 0; i < player_status.lives; ++i)
707 tux_life->draw(565+(18*i),20);
712 sprintf(str, "%2.1f", fps_fps);
713 white_text->draw("FPS", screen->h, 40, 1);
714 gold_text->draw(str, screen->h + 60, 40, 1);
719 GameSession::drawendscreen()
723 if (get_level()->img_bkgd)
724 get_level()->img_bkgd->draw(0, 0);
726 drawgradient(get_level()->bkgd_top, get_level()->bkgd_bottom);
728 blue_text->drawf("GAMEOVER", 0, 200, A_HMIDDLE, A_TOP, 1);
730 sprintf(str, "SCORE: %d", player_status.score);
731 gold_text->drawf(str, 0, 224, A_HMIDDLE, A_TOP, 1);
733 sprintf(str, "COINS: %d", player_status.distros);
734 gold_text->drawf(str, 0, 256, A_HMIDDLE, A_TOP, 1);
739 wait_for_event(event,2000,5000,true);
743 GameSession::drawresultscreen(void)
747 if (get_level()->img_bkgd)
748 get_level()->img_bkgd->draw(0, 0);
750 drawgradient(get_level()->bkgd_top, get_level()->bkgd_bottom);
752 blue_text->drawf("Result:", 0, 200, A_HMIDDLE, A_TOP, 1);
754 sprintf(str, "SCORE: %d", player_status.score);
755 gold_text->drawf(str, 0, 224, A_HMIDDLE, A_TOP, 1);
757 sprintf(str, "COINS: %d", player_status.distros);
758 gold_text->drawf(str, 0, 256, A_HMIDDLE, A_TOP, 1);
763 wait_for_event(event,2000,5000,true);
766 std::string slotinfo(int slot)
771 sprintf(slotfile,"%s/slot%d.stsg",st_save_dir,slot);
773 lisp_object_t* savegame = lisp_read_from_file(slotfile);
776 LispReader reader(lisp_cdr(savegame));
777 reader.read_string("title", &title);
781 if (access(slotfile, F_OK) == 0)
784 snprintf(tmp,1024,"Slot %d - %s",slot, title.c_str());
786 snprintf(tmp, 1024,"Slot %d - Savegame",slot);
789 sprintf(tmp,"Slot %d - Free",slot);