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, 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()
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)
412 GameSession::check_end_conditions()
414 Player* tux = world->get_tux();
417 if (tux->base.x >= World::current()->get_level()->endpos + 32 * (get_level()->use_endsequence ? 22 : 10))
419 exit_status = LEVEL_FINISHED;
421 else if (tux->base.x >= World::current()->get_level()->endpos && !end_sequence)
425 music_manager->halt_music();
429 // Check End conditions
432 player_status.lives -= 1;
434 if (player_status.lives < 0)
436 if(st_gl_mode != ST_GL_TEST)
439 exit_status = GAME_OVER;
442 { // Still has lives, so reset Tux to the levelstart
450 GameSession::action(double frame_ratio)
452 check_end_conditions();
454 if (exit_status == NONE)
456 // Update Tux and the World
457 world->action(frame_ratio);
469 int x = screen->h / 20;
470 for(int i = 0; i < x; ++i)
472 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);
474 fillrect(0,0,screen->w,screen->h,rand() % 50, rand() % 50, rand() % 50, 128);
475 blue_text->drawf("PAUSE - Press 'P' To Play", 0, 230, A_HMIDDLE, A_TOP, 1);
480 Menu::current()->draw();
481 mouse_cursor->draw();
488 GameSession::process_menu()
490 Menu* menu = Menu::current();
495 if(menu == game_menu)
497 switch (game_menu->check())
500 st_pause_ticks_stop();
502 case MNID_ABORTLEVEL:
503 st_pause_ticks_stop();
504 exit_status = LEVEL_ABORT;
508 else if(menu == options_menu)
510 process_options_menu();
512 else if(menu == load_game_menu )
514 process_load_game_menu();
519 GameSession::ExitStatus
522 Menu::set_current(0);
527 update_time = last_update_time = st_get_ticks();
530 clearscreen(0, 0, 0);
533 // Eat unneeded events
535 while (SDL_PollEvent(&event)) {}
539 float overlap = 0.0f;
540 while (exit_status == NONE)
542 /* Calculate the movement-factor */
543 double frame_ratio = ((double)(update_time-last_update_time))/((double)FRAME_RATE);
545 if(!frame_timer.check())
547 frame_timer.start(25);
548 ++global_frame_counter;
552 world->get_tux()->input.old_fire = world->get_tux()->input.fire;
557 // Update the world state and all objects in the world
558 // Do that with a constante time-delta so that the game will run
559 // determistic and not different on different machines
560 if(!game_pause && !Menu::current())
562 frame_ratio *= game_speed;
563 frame_ratio += overlap;
564 while (frame_ratio > 0)
573 overlap = frame_ratio;
583 /* Time stops in pause mode */
584 if(game_pause || Menu::current())
589 /* Set the time of the last update and the time of the current update */
590 last_update_time = update_time;
591 update_time = st_get_ticks();
593 /* Pause till next frame, if the machine running the game is too fast: */
594 /* FIXME: Works great for in OpenGl mode, where the CPU doesn't have to do that much. But
595 the results in SDL mode aren't perfect (thought the 100 FPS are reached), even on an AMD2500+. */
596 if(last_update_time >= update_time - 12)
599 update_time = st_get_ticks();
603 if (!time_left.check() && world->get_tux()->dying == DYING_NOT)
604 world->get_tux()->kill(Player::KILL);
607 if(world->get_tux()->invincible_timer.check())
609 if(world->get_music_type() != HERRING_MUSIC)
610 world->play_music(HERRING_MUSIC);
612 /* are we low on time ? */
613 else if (time_left.get_left() < TIME_WARNING
614 && (world->get_music_type() == LEVEL_MUSIC))
616 world->play_music(HURRYUP_MUSIC);
618 /* or just normal music? */
619 else if(world->get_music_type() != LEVEL_MUSIC)
621 world->play_music(LEVEL_MUSIC);
624 /* Calculate frames per second */
628 fps_fps = (1000.0 / (float)fps_timer.get_gone()) * (float)fps_cnt;
630 if(!fps_timer.check())
632 fps_timer.start(1000);
641 /* Bounce a brick: */
642 void bumpbrick(float x, float y)
644 World::current()->add_bouncy_brick(((int)(x + 1) / 32) * 32,
647 play_sound(sounds[SND_BRICK], SOUND_CENTER_SPEAKER);
652 GameSession::drawstatus()
656 sprintf(str, "%d", player_status.score);
657 white_text->draw("SCORE", 0, 0, 1);
658 gold_text->draw(str, 96, 0, 1);
660 if(st_gl_mode == ST_GL_TEST)
662 white_text->draw("Press ESC To Return",0,20,1);
665 if (time_left.get_left() > TIME_WARNING || (global_frame_counter % 10) < 5)
667 sprintf(str, "%d", time_left.get_left() / 1000 );
668 white_text->draw("TIME", 224, 0, 1);
669 gold_text->draw(str, 304, 0, 1);
672 sprintf(str, "%d", player_status.distros);
673 white_text->draw("COINS", screen->h, 0, 1);
674 gold_text->draw(str, 608, 0, 1);
676 white_text->draw("LIVES", 480, 20);
677 if (player_status.lives >= 5)
679 sprintf(str, "%dx", player_status.lives);
680 gold_text->draw(str, 585, 20);
681 tux_life->draw(565+(18*3), 20);
685 for(int i= 0; i < player_status.lives; ++i)
686 tux_life->draw(565+(18*i),20);
691 sprintf(str, "%2.1f", fps_fps);
692 white_text->draw("FPS", screen->h, 40, 1);
693 gold_text->draw(str, screen->h + 60, 40, 1);
698 GameSession::drawendscreen()
702 if (get_level()->img_bkgd)
703 get_level()->img_bkgd->draw(0, 0);
705 drawgradient(get_level()->bkgd_top, get_level()->bkgd_bottom);
707 blue_text->drawf("GAMEOVER", 0, 200, A_HMIDDLE, A_TOP, 1);
709 sprintf(str, "SCORE: %d", player_status.score);
710 gold_text->drawf(str, 0, 224, A_HMIDDLE, A_TOP, 1);
712 sprintf(str, "COINS: %d", player_status.distros);
713 gold_text->drawf(str, 0, 256, A_HMIDDLE, A_TOP, 1);
718 wait_for_event(event,2000,5000,true);
722 GameSession::drawresultscreen(void)
726 if (get_level()->img_bkgd)
727 get_level()->img_bkgd->draw(0, 0);
729 drawgradient(get_level()->bkgd_top, get_level()->bkgd_bottom);
731 blue_text->drawf("Result:", 0, 200, A_HMIDDLE, A_TOP, 1);
733 sprintf(str, "SCORE: %d", player_status.score);
734 gold_text->drawf(str, 0, 224, A_HMIDDLE, A_TOP, 1);
736 sprintf(str, "COINS: %d", player_status.distros);
737 gold_text->drawf(str, 0, 256, A_HMIDDLE, A_TOP, 1);
742 wait_for_event(event,2000,5000,true);
745 std::string slotinfo(int slot)
750 sprintf(slotfile,"%s/slot%d.stsg",st_save_dir,slot);
752 lisp_object_t* savegame = lisp_read_from_file(slotfile);
753 LispReader reader(lisp_cdr(savegame));
754 reader.read_string("title", &title);
757 if (access(slotfile, F_OK) == 0)
760 snprintf(tmp,1024,"Slot %d - %s",slot, title.c_str());
762 snprintf(tmp, 1024,"Slot %d - Savegame",slot);
765 sprintf(tmp,"Slot %d - Free",slot);