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;
71 frame_timer.init(true);
77 GameSession::restart_level()
80 exit_status = ES_NONE;
81 end_sequence = NO_ENDSEQUENCE;
84 frame_timer.init(true);
89 { // Tux has lost a life, so we try to respawn him at the nearest reset point
90 old_x_pos = world->get_tux()->base.x;
95 if (st_gl_mode == ST_GL_LOAD_LEVEL_FILE)
97 world = new World(subset);
99 else if (st_gl_mode == ST_GL_DEMO_GAME)
101 world = new World(subset);
105 world = new World(subset, levelnb);
108 // Set Tux to the nearest reset point
111 ResetPoint best_reset_point = { -1, -1 };
112 for(std::vector<ResetPoint>::iterator i = get_level()->reset_points.begin();
113 i != get_level()->reset_points.end(); ++i)
115 if (i->x < old_x_pos && best_reset_point.x < i->x)
116 best_reset_point = *i;
119 if (best_reset_point.x != -1)
121 world->get_tux()->base.x = best_reset_point.x;
122 world->get_tux()->base.y = best_reset_point.y;
126 if (st_gl_mode != ST_GL_DEMO_GAME)
128 if(st_gl_mode == ST_GL_PLAY || st_gl_mode == ST_GL_LOAD_LEVEL_FILE)
132 time_left.init(true);
134 world->play_music(LEVEL_MUSIC);
137 GameSession::~GameSession()
143 GameSession::levelintro(void)
145 music_manager->halt_music();
149 get_level()->draw_bg();
151 sprintf(str, "%s", world->get_level()->name.c_str());
152 gold_text->drawf(str, 0, 220, A_HMIDDLE, A_TOP, 1);
154 sprintf(str, "TUX x %d", player_status.lives);
155 white_text->drawf(str, 0, 240, A_HMIDDLE, A_TOP, 1);
157 sprintf(str, "by %s", world->get_level()->author.c_str());
158 white_small_text->drawf(str, 0, 400, A_HMIDDLE, A_TOP, 1);
164 wait_for_event(event,1000,3000,true);
169 GameSession::start_timers()
171 time_left.start(world->get_level()->time_left*1000);
172 st_pause_ticks_init();
173 update_time = st_get_ticks();
177 GameSession::on_escape_press()
179 if(world->get_tux()->dying || end_sequence != NO_ENDSEQUENCE)
180 return; // don't let the player open the menu, when he is dying
184 if(st_gl_mode == ST_GL_TEST)
186 exit_status = ES_LEVEL_ABORT;
188 else if (!Menu::current())
190 /* Tell Tux that the keys are all down, otherwise
191 it could have nasty bugs, like going allways to the right
192 or whatever that key does */
193 Player& tux = *world->get_tux();
194 tux.key_event((SDLKey)keymap.jump, UP);
195 tux.key_event((SDLKey)keymap.duck, UP);
196 tux.key_event((SDLKey)keymap.left, UP);
197 tux.key_event((SDLKey)keymap.right, UP);
198 tux.key_event((SDLKey)keymap.fire, UP);
200 Menu::set_current(game_menu);
201 st_pause_ticks_start();
206 GameSession::process_events()
208 if (end_sequence != NO_ENDSEQUENCE)
210 Player& tux = *world->get_tux();
214 tux.input.right = DOWN;
217 if (int(last_x_pos) == int(tux.base.x))
222 last_x_pos = tux.base.x;
225 while (SDL_PollEvent(&event))
227 /* Check for menu-events, if the menu is shown */
230 Menu::current()->event(event);
232 st_pause_ticks_stop();
237 case SDL_QUIT: /* Quit event - quit: */
238 st_abort("Received window close", "");
241 case SDL_KEYDOWN: /* A keypress! */
243 SDLKey key = event.key.keysym.sym;
247 case SDLK_ESCAPE: /* Escape: Open/Close the menu: */
255 case SDL_JOYBUTTONDOWN:
256 if (event.jbutton.button == joystick_keymap.start_button)
264 if(!Menu::current() && !game_pause)
265 st_pause_ticks_stop();
268 while (SDL_PollEvent(&event))
270 /* Check for menu-events, if the menu is shown */
273 Menu::current()->event(event);
275 st_pause_ticks_stop();
279 Player& tux = *world->get_tux();
283 case SDL_QUIT: /* Quit event - quit: */
284 st_abort("Received window close", "");
287 case SDL_KEYDOWN: /* A keypress! */
289 SDLKey key = event.key.keysym.sym;
291 if(tux.key_event(key,DOWN))
296 case SDLK_ESCAPE: /* Escape: Open/Close the menu: */
304 case SDL_KEYUP: /* A keyrelease! */
306 SDLKey key = event.key.keysym.sym;
308 if(tux.key_event(key, UP))
319 st_pause_ticks_stop();
324 st_pause_ticks_start();
331 tux.size = !tux.size;
334 tux.base.height = 64;
337 tux.base.height = 32;
342 player_status.distros += 50;
346 tux.got_power = tux.FIRE_POWER;
350 tux.got_power = tux.ICE_POWER;
354 tux.invincible_timer.start(TUX_INVINCIBLE_TIME);
358 --player_status.lives;
362 player_status.score += 1000;
375 case SDL_JOYAXISMOTION:
376 if (event.jaxis.axis == joystick_keymap.x_axis)
378 if (event.jaxis.value < -joystick_keymap.dead_zone)
380 tux.input.left = DOWN;
381 tux.input.right = UP;
383 else if (event.jaxis.value > joystick_keymap.dead_zone)
386 tux.input.right = DOWN;
390 tux.input.left = DOWN;
391 tux.input.right = DOWN;
394 else if (event.jaxis.axis == joystick_keymap.y_axis)
396 if (event.jaxis.value > joystick_keymap.dead_zone)
397 tux.input.down = DOWN;
398 else if (event.jaxis.value < -joystick_keymap.dead_zone)
405 case SDL_JOYBUTTONDOWN:
406 if (event.jbutton.button == joystick_keymap.a_button)
408 else if (event.jbutton.button == joystick_keymap.b_button)
409 tux.input.fire = DOWN;
410 else if (event.jbutton.button == joystick_keymap.start_button)
413 case SDL_JOYBUTTONUP:
414 if (event.jbutton.button == joystick_keymap.a_button)
416 else if (event.jbutton.button == joystick_keymap.b_button)
429 GameSession::check_end_conditions()
431 Player* tux = world->get_tux();
434 int endpos = (World::current()->get_level()->width-5) * 32;
435 Tile* endtile = collision_goal(tux->base);
437 // fallback in case the other endpositions don't trigger
438 if (!end_sequence && tux->base.x >= endpos)
440 end_sequence = ENDSEQUENCE_WAITING;
442 music_manager->play_music(level_end_song, 0);
443 endsequence_timer.start(7000);
444 tux->invincible_timer.start(7000); //FIXME: Implement a winning timer for the end sequence (with special winning animation etc.)
446 else if(end_sequence && !endsequence_timer.check())
448 exit_status = ES_LEVEL_FINISHED;
451 else if(end_sequence == ENDSEQUENCE_RUNNING && endtile && endtile->data >= 1)
453 end_sequence = ENDSEQUENCE_WAITING;
455 else if(!end_sequence && endtile && endtile->data == 0)
457 end_sequence = ENDSEQUENCE_RUNNING;
459 music_manager->play_music(level_end_song, 0);
460 endsequence_timer.start(7000); // 5 seconds until we finish the map
461 tux->invincible_timer.start(7000); //FIXME: Implement a winning timer for the end sequence (with special winning animation etc.)
463 else if (!end_sequence && tux->is_dead())
465 player_status.bonus = PlayerStatus::NO_BONUS;
467 if (player_status.lives < 0)
469 exit_status = ES_GAME_OVER;
472 { // Still has lives, so reset Tux to the levelstart
481 GameSession::action(double frame_ratio)
483 if (exit_status == ES_NONE)
485 // Update Tux and the World
486 world->action(frame_ratio);
498 int x = screen->h / 20;
499 for(int i = 0; i < x; ++i)
501 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);
503 fillrect(0,0,screen->w,screen->h,rand() % 50, rand() % 50, rand() % 50, 128);
504 blue_text->drawf("PAUSE - Press 'P' To Play", 0, 230, A_HMIDDLE, A_TOP, 1);
509 Menu::current()->draw();
510 mouse_cursor->draw();
517 GameSession::process_menu()
519 Menu* menu = Menu::current();
524 if(menu == game_menu)
526 switch (game_menu->check())
529 st_pause_ticks_stop();
531 case MNID_ABORTLEVEL:
532 st_pause_ticks_stop();
533 exit_status = ES_LEVEL_ABORT;
537 else if(menu == options_menu)
539 process_options_menu();
541 else if(menu == load_game_menu )
543 process_load_game_menu();
548 GameSession::ExitStatus
551 Menu::set_current(0);
556 update_time = last_update_time = st_get_ticks();
558 // Eat unneeded events
560 while (SDL_PollEvent(&event)) {}
564 while (exit_status == ES_NONE)
566 /* Calculate the movement-factor */
567 double frame_ratio = ((double)(update_time-last_update_time))/((double)FRAME_RATE);
569 if(!frame_timer.check())
571 frame_timer.start(25);
572 ++global_frame_counter;
576 world->get_tux()->input.old_fire = world->get_tux()->input.fire;
581 // Update the world state and all objects in the world
582 // Do that with a constante time-delta so that the game will run
583 // determistic and not different on different machines
584 if(!game_pause && !Menu::current())
587 check_end_conditions();
588 if (end_sequence == ENDSEQUENCE_RUNNING)
589 action(frame_ratio/2);
590 else if(end_sequence == NO_ENDSEQUENCE)
601 /* Time stops in pause mode */
602 if(game_pause || Menu::current())
607 /* Set the time of the last update and the time of the current update */
608 last_update_time = update_time;
609 update_time = st_get_ticks();
611 /* Pause till next frame, if the machine running the game is too fast: */
612 /* FIXME: Works great for in OpenGl mode, where the CPU doesn't have to do that much. But
613 the results in SDL mode aren't perfect (thought the 100 FPS are reached), even on an AMD2500+. */
614 if(last_update_time >= update_time - 12)
617 update_time = st_get_ticks();
621 if (!time_left.check() && world->get_tux()->dying == DYING_NOT
623 world->get_tux()->kill(Player::KILL);
626 if(world->get_tux()->invincible_timer.check() && !end_sequence)
628 world->play_music(HERRING_MUSIC);
630 /* are we low on time ? */
631 else if (time_left.get_left() < TIME_WARNING && !end_sequence)
633 world->play_music(HURRYUP_MUSIC);
635 /* or just normal music? */
636 else if(world->get_music_type() != LEVEL_MUSIC && !end_sequence)
638 world->play_music(LEVEL_MUSIC);
641 /* Calculate frames per second */
645 fps_fps = (1000.0 / (float)fps_timer.get_gone()) * (float)fps_cnt;
647 if(!fps_timer.check())
649 fps_timer.start(1000);
658 /* Bounce a brick: */
659 void bumpbrick(float x, float y)
661 World::current()->add_bouncy_brick(Vector(((int)(x + 1) / 32) * 32,
662 (int)(y / 32) * 32));
664 play_sound(sounds[SND_BRICK], SOUND_CENTER_SPEAKER);
669 GameSession::drawstatus()
673 snprintf(str, 60, "%d", player_status.score);
674 white_text->draw("SCORE", 0, 0, 1);
675 gold_text->draw(str, 96, 0, 1);
677 if(st_gl_mode == ST_GL_TEST)
679 white_text->draw("Press ESC To Return",0,20,1);
682 if(!time_left.check()) {
683 white_text->draw("TIME'S UP", screen->w/2 - white_text->w*8, 0, 1);
684 } else if (time_left.get_left() > TIME_WARNING || (global_frame_counter % 10) < 5) {
685 sprintf(str, "%d", time_left.get_left() / 1000 );
686 white_text->draw("TIME", screen->w/2 - white_text->w*4, 0, 1);
687 gold_text->draw(str, screen->w/2 + gold_text->w, 0, 1);
690 sprintf(str, "%d", player_status.distros);
691 white_text->draw("COINS", screen->w - white_text->w*9, 0, 1);
692 gold_text->draw(str, screen->w - gold_text->w*2, 0, 1);
694 white_text->draw("LIVES", screen->w - white_text->w*9, 20);
695 if (player_status.lives >= 5)
697 sprintf(str, "%dx", player_status.lives);
698 gold_text->draw_align(str, screen->w - gold_text->w, 20, A_RIGHT, A_TOP);
699 tux_life->draw(screen->w - gold_text->w, 20);
703 for(int i= 0; i < player_status.lives; ++i)
704 tux_life->draw(screen->w - tux_life->w*4 +(tux_life->w*i), 20);
709 sprintf(str, "%2.1f", fps_fps);
710 white_text->draw("FPS", screen->w - white_text->w*9, 40, 1);
711 gold_text->draw_align(str, screen->w, 40, A_RIGHT, A_TOP);
716 GameSession::drawresultscreen(void)
720 get_level()->draw_bg();
722 blue_text->drawf("Result:", 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);
736 std::string slotinfo(int slot)
741 sprintf(slotfile,"%s/slot%d.stsg",st_save_dir,slot);
743 lisp_object_t* savegame = lisp_read_from_file(slotfile);
746 LispReader reader(lisp_cdr(savegame));
747 reader.read_string("title", &title);
751 if (access(slotfile, F_OK) == 0)
754 snprintf(tmp,1024,"Slot %d - %s",slot, title.c_str());
756 snprintf(tmp, 1024,"Slot %d - Savegame",slot);
759 sprintf(tmp,"Slot %d - Free",slot);