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 get_level()->draw_bg();
150 sprintf(str, "%s", world->get_level()->name.c_str());
151 gold_text->drawf(str, 0, 220, A_HMIDDLE, A_TOP, 1);
153 sprintf(str, "TUX x %d", player_status.lives);
154 white_text->drawf(str, 0, 240, A_HMIDDLE, A_TOP, 1);
156 sprintf(str, "by %s", world->get_level()->author.c_str());
157 white_small_text->drawf(str, 0, 400, A_HMIDDLE, A_TOP, 1);
163 wait_for_event(event,1000,3000,true);
168 GameSession::start_timers()
170 time_left.start(world->get_level()->time_left*1000);
171 st_pause_ticks_init();
172 update_time = st_get_ticks();
176 GameSession::on_escape_press()
178 if(world->get_tux()->dying || end_sequence != NO_ENDSEQUENCE)
179 return; // don't let the player open the menu, when he is dying
183 if(st_gl_mode == ST_GL_TEST)
185 exit_status = ES_LEVEL_ABORT;
187 else if (!Menu::current())
189 /* Tell Tux that the keys are all down, otherwise
190 it could have nasty bugs, like going allways to the right
191 or whatever that key does */
192 Player& tux = *world->get_tux();
193 tux.key_event((SDLKey)keymap.jump, UP);
194 tux.key_event((SDLKey)keymap.duck, UP);
195 tux.key_event((SDLKey)keymap.left, UP);
196 tux.key_event((SDLKey)keymap.right, UP);
197 tux.key_event((SDLKey)keymap.fire, UP);
199 Menu::set_current(game_menu);
200 st_pause_ticks_start();
205 GameSession::process_events()
207 if (end_sequence != NO_ENDSEQUENCE)
209 Player& tux = *world->get_tux();
213 tux.input.right = DOWN;
216 if (int(last_x_pos) == int(tux.base.x))
221 last_x_pos = tux.base.x;
224 while (SDL_PollEvent(&event))
226 /* Check for menu-events, if the menu is shown */
229 Menu::current()->event(event);
231 st_pause_ticks_stop();
236 case SDL_QUIT: /* Quit event - quit: */
237 st_abort("Received window close", "");
240 case SDL_KEYDOWN: /* A keypress! */
242 SDLKey key = event.key.keysym.sym;
246 case SDLK_ESCAPE: /* Escape: Open/Close the menu: */
254 case SDL_JOYBUTTONDOWN:
255 if (event.jbutton.button == joystick_keymap.start_button)
263 if(!Menu::current() && !game_pause)
264 st_pause_ticks_stop();
267 while (SDL_PollEvent(&event))
269 /* Check for menu-events, if the menu is shown */
272 Menu::current()->event(event);
274 st_pause_ticks_stop();
278 Player& tux = *world->get_tux();
282 case SDL_QUIT: /* Quit event - quit: */
283 st_abort("Received window close", "");
286 case SDL_KEYDOWN: /* A keypress! */
288 SDLKey key = event.key.keysym.sym;
290 if(tux.key_event(key,DOWN))
295 case SDLK_ESCAPE: /* Escape: Open/Close the menu: */
303 case SDL_KEYUP: /* A keyrelease! */
305 SDLKey key = event.key.keysym.sym;
307 if(tux.key_event(key, UP))
318 st_pause_ticks_stop();
323 st_pause_ticks_start();
330 tux.size = !tux.size;
333 tux.base.height = 64;
336 tux.base.height = 32;
341 player_status.distros += 50;
345 tux.got_power = tux.FIRE_POWER;
349 tux.got_power = tux.ICE_POWER;
353 tux.invincible_timer.start(TUX_INVINCIBLE_TIME);
357 --player_status.lives;
361 player_status.score += 1000;
374 case SDL_JOYAXISMOTION:
375 if (event.jaxis.axis == joystick_keymap.x_axis)
377 if (event.jaxis.value < -joystick_keymap.dead_zone)
379 tux.input.left = DOWN;
380 tux.input.right = UP;
382 else if (event.jaxis.value > joystick_keymap.dead_zone)
385 tux.input.right = DOWN;
389 tux.input.left = DOWN;
390 tux.input.right = DOWN;
393 else if (event.jaxis.axis == joystick_keymap.y_axis)
395 if (event.jaxis.value > joystick_keymap.dead_zone)
396 tux.input.down = DOWN;
397 else if (event.jaxis.value < -joystick_keymap.dead_zone)
404 case SDL_JOYBUTTONDOWN:
405 if (event.jbutton.button == joystick_keymap.a_button)
407 else if (event.jbutton.button == joystick_keymap.b_button)
408 tux.input.fire = DOWN;
409 else if (event.jbutton.button == joystick_keymap.start_button)
412 case SDL_JOYBUTTONUP:
413 if (event.jbutton.button == joystick_keymap.a_button)
415 else if (event.jbutton.button == joystick_keymap.b_button)
428 GameSession::check_end_conditions()
430 Player* tux = world->get_tux();
433 int endpos = (World::current()->get_level()->width-5) * 32;
434 Tile* endtile = collision_goal(tux->base);
436 // fallback in case the other endpositions don't trigger
437 if (!end_sequence && tux->base.x >= endpos)
439 end_sequence = ENDSEQUENCE_WAITING;
441 music_manager->play_music(level_end_song, 0);
442 endsequence_timer.start(7000);
443 tux->invincible_timer.start(7000); //FIXME: Implement a winning timer for the end sequence (with special winning animation etc.)
445 else if(end_sequence && !endsequence_timer.check())
447 exit_status = ES_LEVEL_FINISHED;
450 else if(end_sequence == ENDSEQUENCE_RUNNING && endtile && endtile->data >= 1)
452 end_sequence = ENDSEQUENCE_WAITING;
454 else if(!end_sequence && endtile && endtile->data == 0)
456 end_sequence = ENDSEQUENCE_RUNNING;
458 music_manager->play_music(level_end_song, 0);
459 endsequence_timer.start(7000); // 5 seconds until we finish the map
460 tux->invincible_timer.start(7000); //FIXME: Implement a winning timer for the end sequence (with special winning animation etc.)
462 else if (!end_sequence && tux->is_dead())
464 player_status.bonus = PlayerStatus::NO_BONUS;
466 if (player_status.lives < 0)
468 exit_status = ES_GAME_OVER;
471 { // Still has lives, so reset Tux to the levelstart
480 GameSession::action(double frame_ratio)
482 if (exit_status == ES_NONE)
484 // Update Tux and the World
485 world->action(frame_ratio);
497 int x = screen->h / 20;
498 for(int i = 0; i < x; ++i)
500 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);
502 fillrect(0,0,screen->w,screen->h,rand() % 50, rand() % 50, rand() % 50, 128);
503 blue_text->drawf("PAUSE - Press 'P' To Play", 0, 230, A_HMIDDLE, A_TOP, 1);
508 Menu::current()->draw();
509 mouse_cursor->draw();
516 GameSession::process_menu()
518 Menu* menu = Menu::current();
523 if(menu == game_menu)
525 switch (game_menu->check())
528 st_pause_ticks_stop();
530 case MNID_ABORTLEVEL:
531 st_pause_ticks_stop();
532 exit_status = ES_LEVEL_ABORT;
536 else if(menu == options_menu)
538 process_options_menu();
540 else if(menu == load_game_menu )
542 process_load_game_menu();
547 GameSession::ExitStatus
550 Menu::set_current(0);
555 update_time = last_update_time = st_get_ticks();
557 // Eat unneeded events
559 while (SDL_PollEvent(&event)) {}
563 while (exit_status == ES_NONE)
565 /* Calculate the movement-factor */
566 double frame_ratio = ((double)(update_time-last_update_time))/((double)FRAME_RATE);
568 if(!frame_timer.check())
570 frame_timer.start(25);
571 ++global_frame_counter;
575 world->get_tux()->input.old_fire = world->get_tux()->input.fire;
580 // Update the world state and all objects in the world
581 // Do that with a constante time-delta so that the game will run
582 // determistic and not different on different machines
583 if(!game_pause && !Menu::current())
586 check_end_conditions();
587 if (end_sequence == ENDSEQUENCE_RUNNING)
588 action(frame_ratio/2);
589 else if(end_sequence == NO_ENDSEQUENCE)
600 /* Time stops in pause mode */
601 if(game_pause || Menu::current())
606 /* Set the time of the last update and the time of the current update */
607 last_update_time = update_time;
608 update_time = st_get_ticks();
610 /* Pause till next frame, if the machine running the game is too fast: */
611 /* FIXME: Works great for in OpenGl mode, where the CPU doesn't have to do that much. But
612 the results in SDL mode aren't perfect (thought the 100 FPS are reached), even on an AMD2500+. */
613 if(last_update_time >= update_time - 12)
616 update_time = st_get_ticks();
620 if (!time_left.check() && world->get_tux()->dying == DYING_NOT
622 world->get_tux()->kill(Player::KILL);
625 if(world->get_tux()->invincible_timer.check() && !end_sequence)
627 world->play_music(HERRING_MUSIC);
629 /* are we low on time ? */
630 else if (time_left.get_left() < TIME_WARNING && !end_sequence)
632 world->play_music(HURRYUP_MUSIC);
634 /* or just normal music? */
635 else if(world->get_music_type() != LEVEL_MUSIC && !end_sequence)
637 world->play_music(LEVEL_MUSIC);
640 /* Calculate frames per second */
644 fps_fps = (1000.0 / (float)fps_timer.get_gone()) * (float)fps_cnt;
646 if(!fps_timer.check())
648 fps_timer.start(1000);
657 /* Bounce a brick: */
658 void bumpbrick(float x, float y)
660 World::current()->add_bouncy_brick(Vector(((int)(x + 1) / 32) * 32,
661 (int)(y / 32) * 32));
663 play_sound(sounds[SND_BRICK], SOUND_CENTER_SPEAKER);
668 GameSession::drawstatus()
672 sprintf(str, "%d", player_status.score);
673 white_text->draw("SCORE", 0, 0, 1);
674 gold_text->draw(str, 96, 0, 1);
676 if(st_gl_mode == ST_GL_TEST)
678 white_text->draw("Press ESC To Return",0,20,1);
681 if(!time_left.check()) {
682 white_text->draw("TIME'S UP", screen->w/2 - white_text->w*8, 0, 1);
683 } else if (time_left.get_left() > TIME_WARNING || (global_frame_counter % 10) < 5) {
684 sprintf(str, "%d", time_left.get_left() / 1000 );
685 white_text->draw("TIME", screen->w/2 - white_text->w*4, 0, 1);
686 gold_text->draw(str, screen->w/2 + gold_text->w, 0, 1);
689 sprintf(str, "%d", player_status.distros);
690 white_text->draw("COINS", screen->w - white_text->w*9, 0, 1);
691 gold_text->draw(str, screen->w - gold_text->w*2, 0, 1);
693 white_text->draw("LIVES", screen->w - white_text->w*9, 20);
694 if (player_status.lives >= 5)
696 sprintf(str, "%dx", player_status.lives);
697 gold_text->draw_align(str, screen->w - gold_text->w, 20, A_RIGHT, A_TOP);
698 tux_life->draw(screen->w - gold_text->w, 20);
702 for(int i= 0; i < player_status.lives; ++i)
703 tux_life->draw(screen->w - tux_life->w*4 +(tux_life->w*i), 20);
708 sprintf(str, "%2.1f", fps_fps);
709 white_text->draw("FPS", screen->w - white_text->w*9, 40, 1);
710 gold_text->draw_align(str, screen->w, 40, A_RIGHT, A_TOP);
715 GameSession::drawresultscreen(void)
719 get_level()->draw_bg();
721 blue_text->drawf("Result:", 0, 200, A_HMIDDLE, A_TOP, 1);
723 sprintf(str, "SCORE: %d", player_status.score);
724 gold_text->drawf(str, 0, 224, A_HMIDDLE, A_TOP, 1);
726 sprintf(str, "COINS: %d", player_status.distros);
727 gold_text->drawf(str, 0, 256, A_HMIDDLE, A_TOP, 1);
732 wait_for_event(event,2000,5000,true);
735 std::string slotinfo(int slot)
740 sprintf(slotfile,"%s/slot%d.stsg",st_save_dir,slot);
742 lisp_object_t* savegame = lisp_read_from_file(slotfile);
745 LispReader reader(lisp_cdr(savegame));
746 reader.read_string("title", &title);
750 if (access(slotfile, F_OK) == 0)
753 snprintf(tmp,1024,"Slot %d - %s",slot, title.c_str());
755 snprintf(tmp, 1024,"Slot %d - Savegame",slot);
758 sprintf(tmp,"Slot %d - Free",slot);