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(false),
66 global_frame_counter = 0;
70 frame_timer.init(true);
76 GameSession::restart_level()
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()
199 Player& tux = *world->get_tux();
202 tux.input.right = DOWN;
205 if (int(last_x_pos) == int(tux.base.x))
210 last_x_pos = tux.base.x;
213 while (SDL_PollEvent(&event))
215 /* Check for menu-events, if the menu is shown */
218 Menu::current()->event(event);
219 st_pause_ticks_start();
224 case SDL_QUIT: /* Quit event - quit: */
225 st_abort("Received window close", "");
228 case SDL_KEYDOWN: /* A keypress! */
230 SDLKey key = event.key.keysym.sym;
234 case SDLK_ESCAPE: /* Escape: Open/Close the menu: */
242 case SDL_JOYBUTTONDOWN:
243 if (event.jbutton.button == joystick_keymap.start_button)
251 if(!Menu::current() && !game_pause)
252 st_pause_ticks_stop();
255 while (SDL_PollEvent(&event))
257 /* Check for menu-events, if the menu is shown */
260 Menu::current()->event(event);
261 st_pause_ticks_start();
265 Player& tux = *world->get_tux();
269 case SDL_QUIT: /* Quit event - quit: */
270 st_abort("Received window close", "");
273 case SDL_KEYDOWN: /* A keypress! */
275 SDLKey key = event.key.keysym.sym;
277 if(tux.key_event(key,DOWN))
282 case SDLK_ESCAPE: /* Escape: Open/Close the menu: */
290 case SDL_KEYUP: /* A keyrelease! */
292 SDLKey key = event.key.keysym.sym;
294 if(tux.key_event(key, UP))
305 st_pause_ticks_stop();
310 st_pause_ticks_start();
317 tux.size = !tux.size;
320 tux.base.height = 64;
323 tux.base.height = 32;
328 player_status.distros += 50;
336 tux.invincible_timer.start(TUX_INVINCIBLE_TIME);
340 --player_status.lives;
344 player_status.score += 1000;
357 case SDL_JOYAXISMOTION:
358 if (event.jaxis.axis == joystick_keymap.x_axis)
360 if (event.jaxis.value < -joystick_keymap.dead_zone)
362 tux.input.left = DOWN;
363 tux.input.right = UP;
365 else if (event.jaxis.value > joystick_keymap.dead_zone)
368 tux.input.right = DOWN;
372 tux.input.left = DOWN;
373 tux.input.right = DOWN;
376 else if (event.jaxis.axis == joystick_keymap.y_axis)
378 if (event.jaxis.value > joystick_keymap.dead_zone)
379 tux.input.down = DOWN;
380 else if (event.jaxis.value < -joystick_keymap.dead_zone)
387 case SDL_JOYBUTTONDOWN:
388 if (event.jbutton.button == joystick_keymap.a_button)
390 else if (event.jbutton.button == joystick_keymap.b_button)
391 tux.input.fire = DOWN;
392 else if (event.jbutton.button == joystick_keymap.start_button)
395 case SDL_JOYBUTTONUP:
396 if (event.jbutton.button == joystick_keymap.a_button)
398 else if (event.jbutton.button == joystick_keymap.b_button)
411 GameSession::check_end_conditions()
413 Player* tux = world->get_tux();
416 int endpos = (World::current()->get_level()->width-5) * 32;
417 Tile* endtile = collision_goal(tux->base);
419 // fallback in case the other endpositions don't trigger
420 if (tux->base.x >= endpos || (endtile && endtile->data >= 1)
421 || (end_sequence && !endsequence_timer.check()))
423 exit_status = LEVEL_FINISHED;
426 else if(!end_sequence && endtile && endtile->data == 0)
430 music_manager->halt_music();
431 music_manager->play_music(level_end_song);
432 endsequence_timer.start(5000); // 5 seconds until we finish the map
434 else if (!end_sequence && tux->is_dead())
436 player_status.bonus = PlayerStatus::NO_BONUS;
437 player_status.lives -= 1;
439 if (player_status.lives < 0)
441 if(st_gl_mode != ST_GL_TEST)
444 exit_status = GAME_OVER;
447 { // Still has lives, so reset Tux to the levelstart
456 GameSession::action(double frame_ratio)
458 check_end_conditions();
460 if (exit_status == NONE)
462 // Update Tux and the World
463 world->action(frame_ratio);
475 int x = screen->h / 20;
476 for(int i = 0; i < x; ++i)
478 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);
480 fillrect(0,0,screen->w,screen->h,rand() % 50, rand() % 50, rand() % 50, 128);
481 blue_text->drawf("PAUSE - Press 'P' To Play", 0, 230, A_HMIDDLE, A_TOP, 1);
486 Menu::current()->draw();
487 mouse_cursor->draw();
494 GameSession::process_menu()
496 Menu* menu = Menu::current();
501 if(menu == game_menu)
503 switch (game_menu->check())
506 st_pause_ticks_stop();
508 case MNID_ABORTLEVEL:
509 st_pause_ticks_stop();
510 exit_status = LEVEL_ABORT;
514 else if(menu == options_menu)
516 process_options_menu();
518 else if(menu == load_game_menu )
520 process_load_game_menu();
525 GameSession::ExitStatus
528 Menu::set_current(0);
533 update_time = last_update_time = st_get_ticks();
536 // clearscreen(0, 0, 0);
539 // Eat unneeded events
541 while (SDL_PollEvent(&event)) {}
545 float overlap = 0.0f;
546 while (exit_status == NONE)
548 /* Calculate the movement-factor */
549 double frame_ratio = ((double)(update_time-last_update_time))/((double)FRAME_RATE);
551 if(!frame_timer.check())
553 frame_timer.start(25);
554 ++global_frame_counter;
558 world->get_tux()->input.old_fire = world->get_tux()->input.fire;
563 // Update the world state and all objects in the world
564 // Do that with a constante time-delta so that the game will run
565 // determistic and not different on different machines
566 if(!game_pause && !Menu::current())
568 frame_ratio *= game_speed;
569 frame_ratio += overlap;
570 while (frame_ratio > 0)
579 overlap = frame_ratio;
589 /* Time stops in pause mode */
590 if(game_pause || Menu::current())
595 /* Set the time of the last update and the time of the current update */
596 last_update_time = update_time;
597 update_time = st_get_ticks();
599 /* Pause till next frame, if the machine running the game is too fast: */
600 /* FIXME: Works great for in OpenGl mode, where the CPU doesn't have to do that much. But
601 the results in SDL mode aren't perfect (thought the 100 FPS are reached), even on an AMD2500+. */
602 if(last_update_time >= update_time - 12)
605 update_time = st_get_ticks();
609 if (!time_left.check() && world->get_tux()->dying == DYING_NOT)
610 world->get_tux()->kill(Player::KILL);
613 if(world->get_tux()->invincible_timer.check())
615 if(world->get_music_type() != HERRING_MUSIC)
616 world->play_music(HERRING_MUSIC);
618 /* are we low on time ? */
619 else if (time_left.get_left() < TIME_WARNING)
621 world->play_music(HURRYUP_MUSIC);
623 /* or just normal music? */
624 else if(world->get_music_type() != LEVEL_MUSIC)
626 world->play_music(LEVEL_MUSIC);
629 /* Calculate frames per second */
633 fps_fps = (1000.0 / (float)fps_timer.get_gone()) * (float)fps_cnt;
635 if(!fps_timer.check())
637 fps_timer.start(1000);
646 /* Bounce a brick: */
647 void bumpbrick(float x, float y)
649 World::current()->add_bouncy_brick(((int)(x + 1) / 32) * 32,
652 play_sound(sounds[SND_BRICK], SOUND_CENTER_SPEAKER);
657 GameSession::drawstatus()
661 sprintf(str, "%d", player_status.score);
662 white_text->draw("SCORE", 0, 0, 1);
663 gold_text->draw(str, 96, 0, 1);
665 if(st_gl_mode == ST_GL_TEST)
667 white_text->draw("Press ESC To Return",0,20,1);
670 if(!time_left.check()) {
671 white_text->draw("TIME'S UP", 224, 0, 1);
672 } else if (time_left.get_left() > TIME_WARNING || (global_frame_counter % 10) < 5) {
673 sprintf(str, "%d", time_left.get_left() / 1000 );
674 white_text->draw("TIME", 224, 0, 1);
675 gold_text->draw(str, 304, 0, 1);
678 sprintf(str, "%d", player_status.distros);
679 white_text->draw("COINS", screen->h, 0, 1);
680 gold_text->draw(str, 608, 0, 1);
682 white_text->draw("LIVES", 480, 20);
683 if (player_status.lives >= 5)
685 sprintf(str, "%dx", player_status.lives);
686 gold_text->draw(str, 585, 20);
687 tux_life->draw(565+(18*3), 20);
691 for(int i= 0; i < player_status.lives; ++i)
692 tux_life->draw(565+(18*i),20);
697 sprintf(str, "%2.1f", fps_fps);
698 white_text->draw("FPS", screen->h, 40, 1);
699 gold_text->draw(str, screen->h + 60, 40, 1);
704 GameSession::drawendscreen()
708 if (get_level()->img_bkgd)
709 get_level()->img_bkgd->draw(0, 0);
711 drawgradient(get_level()->bkgd_top, get_level()->bkgd_bottom);
713 blue_text->drawf("GAMEOVER", 0, 200, A_HMIDDLE, A_TOP, 1);
715 sprintf(str, "SCORE: %d", player_status.score);
716 gold_text->drawf(str, 0, 224, A_HMIDDLE, A_TOP, 1);
718 sprintf(str, "COINS: %d", player_status.distros);
719 gold_text->drawf(str, 0, 256, A_HMIDDLE, A_TOP, 1);
724 wait_for_event(event,2000,5000,true);
728 GameSession::drawresultscreen(void)
732 if (get_level()->img_bkgd)
733 get_level()->img_bkgd->draw(0, 0);
735 drawgradient(get_level()->bkgd_top, get_level()->bkgd_bottom);
737 blue_text->drawf("Result:", 0, 200, A_HMIDDLE, A_TOP, 1);
739 sprintf(str, "SCORE: %d", player_status.score);
740 gold_text->drawf(str, 0, 224, A_HMIDDLE, A_TOP, 1);
742 sprintf(str, "COINS: %d", player_status.distros);
743 gold_text->drawf(str, 0, 256, A_HMIDDLE, A_TOP, 1);
748 wait_for_event(event,2000,5000,true);
751 std::string slotinfo(int slot)
756 sprintf(slotfile,"%s/slot%d.stsg",st_save_dir,slot);
758 lisp_object_t* savegame = lisp_read_from_file(slotfile);
761 LispReader reader(lisp_cdr(savegame));
762 reader.read_string("title", &title);
766 if (access(slotfile, F_OK) == 0)
769 snprintf(tmp,1024,"Slot %d - %s",slot, title.c_str());
771 snprintf(tmp, 1024,"Slot %d - Savegame",slot);
774 sprintf(tmp,"Slot %d - Free",slot);