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();
544 // clearscreen(0, 0, 0);
547 // Eat unneeded events
549 while (SDL_PollEvent(&event)) {}
553 float overlap = 0.0f;
554 while (exit_status == NONE)
556 /* Calculate the movement-factor */
557 double frame_ratio = ((double)(update_time-last_update_time))/((double)FRAME_RATE);
559 if(!frame_timer.check())
561 frame_timer.start(25);
562 ++global_frame_counter;
566 world->get_tux()->input.old_fire = world->get_tux()->input.fire;
571 // Update the world state and all objects in the world
572 // Do that with a constante time-delta so that the game will run
573 // determistic and not different on different machines
574 if(!game_pause && !Menu::current())
576 frame_ratio *= game_speed;
577 frame_ratio += overlap;
578 while (frame_ratio > 0)
581 check_end_conditions();
582 if (end_sequence == ENDSEQUENCE_RUNNING)
584 else if(end_sequence == NO_ENDSEQUENCE)
588 overlap = frame_ratio;
598 /* Time stops in pause mode */
599 if(game_pause || Menu::current())
604 /* Set the time of the last update and the time of the current update */
605 last_update_time = update_time;
606 update_time = st_get_ticks();
608 /* Pause till next frame, if the machine running the game is too fast: */
609 /* FIXME: Works great for in OpenGl mode, where the CPU doesn't have to do that much. But
610 the results in SDL mode aren't perfect (thought the 100 FPS are reached), even on an AMD2500+. */
611 if(last_update_time >= update_time - 12)
614 update_time = st_get_ticks();
618 if (!time_left.check() && world->get_tux()->dying == DYING_NOT)
619 world->get_tux()->kill(Player::KILL);
622 if(world->get_tux()->invincible_timer.check())
624 if(world->get_music_type() != HERRING_MUSIC)
625 world->play_music(HERRING_MUSIC);
627 /* are we low on time ? */
628 else if (time_left.get_left() < TIME_WARNING)
630 world->play_music(HURRYUP_MUSIC);
632 /* or just normal music? */
633 else if(world->get_music_type() != LEVEL_MUSIC)
635 world->play_music(LEVEL_MUSIC);
638 /* Calculate frames per second */
642 fps_fps = (1000.0 / (float)fps_timer.get_gone()) * (float)fps_cnt;
644 if(!fps_timer.check())
646 fps_timer.start(1000);
655 /* Bounce a brick: */
656 void bumpbrick(float x, float y)
658 World::current()->add_bouncy_brick(((int)(x + 1) / 32) * 32,
661 play_sound(sounds[SND_BRICK], SOUND_CENTER_SPEAKER);
666 GameSession::drawstatus()
670 sprintf(str, "%d", player_status.score);
671 white_text->draw("SCORE", 0, 0, 1);
672 gold_text->draw(str, 96, 0, 1);
674 if(st_gl_mode == ST_GL_TEST)
676 white_text->draw("Press ESC To Return",0,20,1);
679 if(!time_left.check()) {
680 white_text->draw("TIME'S UP", 224, 0, 1);
681 } else if (time_left.get_left() > TIME_WARNING || (global_frame_counter % 10) < 5) {
682 sprintf(str, "%d", time_left.get_left() / 1000 );
683 white_text->draw("TIME", 224, 0, 1);
684 gold_text->draw(str, 304, 0, 1);
687 sprintf(str, "%d", player_status.distros);
688 white_text->draw("COINS", screen->h, 0, 1);
689 gold_text->draw(str, 608, 0, 1);
691 white_text->draw("LIVES", 480, 20);
692 if (player_status.lives >= 5)
694 sprintf(str, "%dx", player_status.lives);
695 gold_text->draw_align(str, 617, 20, A_RIGHT, A_TOP);
696 tux_life->draw(565+(18*3), 20);
700 for(int i= 0; i < player_status.lives; ++i)
701 tux_life->draw(565+(18*i),20);
706 sprintf(str, "%2.1f", fps_fps);
707 white_text->draw("FPS", screen->h, 40, 1);
708 gold_text->draw(str, screen->h + 60, 40, 1);
713 GameSession::drawendscreen()
717 if (get_level()->img_bkgd)
718 get_level()->img_bkgd->draw(0, 0);
720 drawgradient(get_level()->bkgd_top, get_level()->bkgd_bottom);
722 blue_text->drawf("GAMEOVER", 0, 200, A_HMIDDLE, A_TOP, 1);
724 sprintf(str, "SCORE: %d", player_status.score);
725 gold_text->drawf(str, 0, 224, A_HMIDDLE, A_TOP, 1);
727 sprintf(str, "COINS: %d", player_status.distros);
728 gold_text->drawf(str, 0, 256, A_HMIDDLE, A_TOP, 1);
733 wait_for_event(event,2000,5000,true);
737 GameSession::drawresultscreen(void)
741 if (get_level()->img_bkgd)
742 get_level()->img_bkgd->draw(0, 0);
744 drawgradient(get_level()->bkgd_top, get_level()->bkgd_bottom);
746 blue_text->drawf("Result:", 0, 200, A_HMIDDLE, A_TOP, 1);
748 sprintf(str, "SCORE: %d", player_status.score);
749 gold_text->drawf(str, 0, 224, A_HMIDDLE, A_TOP, 1);
751 sprintf(str, "COINS: %d", player_status.distros);
752 gold_text->drawf(str, 0, 256, A_HMIDDLE, A_TOP, 1);
757 wait_for_event(event,2000,5000,true);
760 std::string slotinfo(int slot)
765 sprintf(slotfile,"%s/slot%d.stsg",st_save_dir,slot);
767 lisp_object_t* savegame = lisp_read_from_file(slotfile);
770 LispReader reader(lisp_cdr(savegame));
771 reader.read_string("title", &title);
775 if (access(slotfile, F_OK) == 0)
778 snprintf(tmp,1024,"Slot %d - %s",slot, title.c_str());
780 snprintf(tmp, 1024,"Slot %d - Savegame",slot);
783 sprintf(tmp,"Slot %d - Free",slot);