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-10) * 32;
417 Tile* endtile = collision_goal(tux->base);
418 //printf("EndTile: %p.\n", endtile);
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 endsequence_timer.start(5000); // 5 seconds until we finish the map
433 else if (!end_sequence && tux->is_dead())
435 player_status.lives -= 1;
437 if (player_status.lives < 0)
439 if(st_gl_mode != ST_GL_TEST)
442 exit_status = GAME_OVER;
445 { // Still has lives, so reset Tux to the levelstart
454 GameSession::action(double frame_ratio)
456 check_end_conditions();
458 if (exit_status == NONE)
460 // Update Tux and the World
461 world->action(frame_ratio);
473 int x = screen->h / 20;
474 for(int i = 0; i < x; ++i)
476 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);
478 fillrect(0,0,screen->w,screen->h,rand() % 50, rand() % 50, rand() % 50, 128);
479 blue_text->drawf("PAUSE - Press 'P' To Play", 0, 230, A_HMIDDLE, A_TOP, 1);
484 Menu::current()->draw();
485 mouse_cursor->draw();
492 GameSession::process_menu()
494 Menu* menu = Menu::current();
499 if(menu == game_menu)
501 switch (game_menu->check())
504 st_pause_ticks_stop();
506 case MNID_ABORTLEVEL:
507 st_pause_ticks_stop();
508 exit_status = LEVEL_ABORT;
512 else if(menu == options_menu)
514 process_options_menu();
516 else if(menu == load_game_menu )
518 process_load_game_menu();
523 GameSession::ExitStatus
526 Menu::set_current(0);
531 update_time = last_update_time = st_get_ticks();
534 // clearscreen(0, 0, 0);
537 // Eat unneeded events
539 while (SDL_PollEvent(&event)) {}
543 float overlap = 0.0f;
544 while (exit_status == NONE)
546 /* Calculate the movement-factor */
547 double frame_ratio = ((double)(update_time-last_update_time))/((double)FRAME_RATE);
549 if(!frame_timer.check())
551 frame_timer.start(25);
552 ++global_frame_counter;
556 world->get_tux()->input.old_fire = world->get_tux()->input.fire;
561 // Update the world state and all objects in the world
562 // Do that with a constante time-delta so that the game will run
563 // determistic and not different on different machines
564 if(!game_pause && !Menu::current())
566 frame_ratio *= game_speed;
567 frame_ratio += overlap;
568 while (frame_ratio > 0)
577 overlap = frame_ratio;
587 /* Time stops in pause mode */
588 if(game_pause || Menu::current())
593 /* Set the time of the last update and the time of the current update */
594 last_update_time = update_time;
595 update_time = st_get_ticks();
597 /* Pause till next frame, if the machine running the game is too fast: */
598 /* FIXME: Works great for in OpenGl mode, where the CPU doesn't have to do that much. But
599 the results in SDL mode aren't perfect (thought the 100 FPS are reached), even on an AMD2500+. */
600 if(last_update_time >= update_time - 12)
603 update_time = st_get_ticks();
607 if (!time_left.check() && world->get_tux()->dying == DYING_NOT)
608 world->get_tux()->kill(Player::KILL);
611 if(world->get_tux()->invincible_timer.check())
613 if(world->get_music_type() != HERRING_MUSIC)
614 world->play_music(HERRING_MUSIC);
616 /* are we low on time ? */
617 else if (time_left.get_left() < TIME_WARNING)
619 world->play_music(HURRYUP_MUSIC);
621 /* or just normal music? */
622 else if(world->get_music_type() != LEVEL_MUSIC)
624 world->play_music(LEVEL_MUSIC);
627 /* Calculate frames per second */
631 fps_fps = (1000.0 / (float)fps_timer.get_gone()) * (float)fps_cnt;
633 if(!fps_timer.check())
635 fps_timer.start(1000);
644 /* Bounce a brick: */
645 void bumpbrick(float x, float y)
647 World::current()->add_bouncy_brick(((int)(x + 1) / 32) * 32,
650 play_sound(sounds[SND_BRICK], SOUND_CENTER_SPEAKER);
655 GameSession::drawstatus()
659 sprintf(str, "%d", player_status.score);
660 white_text->draw("SCORE", 0, 0, 1);
661 gold_text->draw(str, 96, 0, 1);
663 if(st_gl_mode == ST_GL_TEST)
665 white_text->draw("Press ESC To Return",0,20,1);
668 if(!time_left.check()) {
669 white_text->draw("TIME'S UP", 224, 0, 1);
670 } else if (time_left.get_left() > TIME_WARNING || (global_frame_counter % 10) < 5) {
671 sprintf(str, "%d", time_left.get_left() / 1000 );
672 white_text->draw("TIME", 224, 0, 1);
673 gold_text->draw(str, 304, 0, 1);
676 sprintf(str, "%d", player_status.distros);
677 white_text->draw("COINS", screen->h, 0, 1);
678 gold_text->draw(str, 608, 0, 1);
680 white_text->draw("LIVES", 480, 20);
681 if (player_status.lives >= 5)
683 sprintf(str, "%dx", player_status.lives);
684 gold_text->draw(str, 585, 20);
685 tux_life->draw(565+(18*3), 20);
689 for(int i= 0; i < player_status.lives; ++i)
690 tux_life->draw(565+(18*i),20);
695 sprintf(str, "%2.1f", fps_fps);
696 white_text->draw("FPS", screen->h, 40, 1);
697 gold_text->draw(str, screen->h + 60, 40, 1);
702 GameSession::drawendscreen()
706 if (get_level()->img_bkgd)
707 get_level()->img_bkgd->draw(0, 0);
709 drawgradient(get_level()->bkgd_top, get_level()->bkgd_bottom);
711 blue_text->drawf("GAMEOVER", 0, 200, A_HMIDDLE, A_TOP, 1);
713 sprintf(str, "SCORE: %d", player_status.score);
714 gold_text->drawf(str, 0, 224, A_HMIDDLE, A_TOP, 1);
716 sprintf(str, "COINS: %d", player_status.distros);
717 gold_text->drawf(str, 0, 256, A_HMIDDLE, A_TOP, 1);
722 wait_for_event(event,2000,5000,true);
726 GameSession::drawresultscreen(void)
730 if (get_level()->img_bkgd)
731 get_level()->img_bkgd->draw(0, 0);
733 drawgradient(get_level()->bkgd_top, get_level()->bkgd_bottom);
735 blue_text->drawf("Result:", 0, 200, A_HMIDDLE, A_TOP, 1);
737 sprintf(str, "SCORE: %d", player_status.score);
738 gold_text->drawf(str, 0, 224, A_HMIDDLE, A_TOP, 1);
740 sprintf(str, "COINS: %d", player_status.distros);
741 gold_text->drawf(str, 0, 256, A_HMIDDLE, A_TOP, 1);
746 wait_for_event(event,2000,5000,true);
749 std::string slotinfo(int slot)
754 sprintf(slotfile,"%s/slot%d.stsg",st_save_dir,slot);
756 lisp_object_t* savegame = lisp_read_from_file(slotfile);
759 LispReader reader(lisp_cdr(savegame));
760 reader.read_string("title", &title);
764 if (access(slotfile, F_OK) == 0)
767 snprintf(tmp,1024,"Slot %d - %s",slot, title.c_str());
769 snprintf(tmp, 1024,"Slot %d - Savegame",slot);
772 sprintf(tmp,"Slot %d - Free",slot);