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>
42 #include "screen/screen.h"
44 #include "high_scores.h"
52 #include "collision.h"
54 #include "particlesystem.h"
55 #include "resources.h"
56 #include "background.h"
60 GameSession* GameSession::current_ = 0;
62 GameSession::GameSession(const std::string& levelname_, int mode)
63 : level(0), currentsector(0), st_gl_mode(mode),
64 end_sequence(NO_ENDSEQUENCE), levelname(levelname_)
68 global_frame_counter = 0;
73 frame_timer.init(true);
75 context = new DrawingContext();
81 GameSession::restart_level()
84 exit_status = ES_NONE;
85 end_sequence = NO_ENDSEQUENCE;
88 frame_timer.init(true);
93 { // Tux has lost a life, so we try to respawn him at the nearest reset point
94 old_x_pos = world->get_tux()->base.x;
102 level->load(levelname);
103 currentsector = level->get_sector("main");
105 st_abort("Level has no main sector.", "");
106 currentsector->activate("main");
109 // Set Tux to the nearest reset point
112 ResetPoint best_reset_point = { -1, -1 };
113 for(std::vector<ResetPoint>::iterator i = get_level()->reset_points.begin();
114 i != get_level()->reset_points.end(); ++i)
116 if (i->x < old_x_pos && best_reset_point.x < i->x)
117 best_reset_point = *i;
120 if (best_reset_point.x != -1)
122 world->get_tux()->base.x = best_reset_point.x;
123 world->get_tux()->base.y = best_reset_point.y;
128 if (st_gl_mode != ST_GL_DEMO_GAME)
130 if(st_gl_mode == ST_GL_PLAY || st_gl_mode == ST_GL_LOAD_LEVEL_FILE)
134 time_left.init(true);
136 currentsector->play_music(LEVEL_MUSIC);
139 GameSession::~GameSession()
146 GameSession::levelintro(void)
148 sound_manager->halt_music();
152 DrawingContext context;
153 currentsector->background->draw(context);
155 context.draw_text_center(gold_text, level->get_name(), Vector(0, 220),
158 sprintf(str, "TUX x %d", player_status.lives);
159 context.draw_text_center(white_text, str, Vector(0, 240),
162 context.draw_text_center(white_small_text,
163 std::string(_("by ")) + level->get_author(),
164 Vector(0, 400), LAYER_FOREGROUND1);
166 context.do_drawing();
169 wait_for_event(event,1000,3000,true);
174 GameSession::start_timers()
176 time_left.start(level->time_left*1000);
177 st_pause_ticks_init();
178 update_time = st_get_ticks();
182 GameSession::on_escape_press()
184 if(currentsector->player->dying || end_sequence != NO_ENDSEQUENCE)
185 return; // don't let the player open the menu, when he is dying
190 if(st_gl_mode == ST_GL_TEST)
192 exit_status = ES_LEVEL_ABORT;
194 else if (!Menu::current())
196 /* Tell Tux that the keys are all down, otherwise
197 it could have nasty bugs, like going allways to the right
198 or whatever that key does */
199 Player& tux = *(currentsector->player);
200 tux.key_event((SDLKey)keymap.jump, UP);
201 tux.key_event((SDLKey)keymap.duck, UP);
202 tux.key_event((SDLKey)keymap.left, UP);
203 tux.key_event((SDLKey)keymap.right, UP);
204 tux.key_event((SDLKey)keymap.fire, UP);
206 Menu::set_current(game_menu);
207 st_pause_ticks_start();
212 GameSession::process_events()
214 if (end_sequence != NO_ENDSEQUENCE)
216 Player& tux = *currentsector->player;
220 tux.input.right = DOWN;
223 if (int(last_x_pos) == int(tux.base.x))
228 last_x_pos = tux.base.x;
231 while (SDL_PollEvent(&event))
233 /* Check for menu-events, if the menu is shown */
236 Menu::current()->event(event);
238 st_pause_ticks_stop();
243 case SDL_QUIT: /* Quit event - quit: */
244 st_abort("Received window close", "");
247 case SDL_KEYDOWN: /* A keypress! */
249 SDLKey key = event.key.keysym.sym;
253 case SDLK_ESCAPE: /* Escape: Open/Close the menu: */
261 case SDL_JOYBUTTONDOWN:
262 if (event.jbutton.button == joystick_keymap.start_button)
270 if(!Menu::current() && !game_pause)
271 st_pause_ticks_stop();
274 while (SDL_PollEvent(&event))
276 /* Check for menu-events, if the menu is shown */
279 Menu::current()->event(event);
281 st_pause_ticks_stop();
285 Player& tux = *currentsector->player;
289 case SDL_QUIT: /* Quit event - quit: */
290 st_abort("Received window close", "");
293 case SDL_KEYDOWN: /* A keypress! */
295 SDLKey key = event.key.keysym.sym;
297 if(tux.key_event(key,DOWN))
302 case SDLK_ESCAPE: /* Escape: Open/Close the menu: */
310 case SDL_KEYUP: /* A keyrelease! */
312 SDLKey key = event.key.keysym.sym;
314 if(tux.key_event(key, UP))
323 snprintf(buf, sizeof(buf), "P: %4.1f,%4.1f",
324 tux.base.x, tux.base.y);
325 context->draw_text(white_text, buf,
326 Vector(0, screen->h - white_text->get_height()),
328 context->do_drawing();
338 st_pause_ticks_stop();
343 st_pause_ticks_start();
355 player_status.distros += 50;
359 tux.got_power = tux.FIRE_POWER;
363 tux.got_power = tux.ICE_POWER;
367 tux.invincible_timer.start(TUX_INVINCIBLE_TIME);
371 --player_status.lives;
375 player_status.score += 1000;
388 case SDL_JOYAXISMOTION:
389 if (event.jaxis.axis == joystick_keymap.x_axis)
391 if (event.jaxis.value < -joystick_keymap.dead_zone)
393 tux.input.left = DOWN;
394 tux.input.right = UP;
396 else if (event.jaxis.value > joystick_keymap.dead_zone)
399 tux.input.right = DOWN;
403 tux.input.left = DOWN;
404 tux.input.right = DOWN;
407 else if (event.jaxis.axis == joystick_keymap.y_axis)
409 if (event.jaxis.value > joystick_keymap.dead_zone)
410 tux.input.down = DOWN;
411 else if (event.jaxis.value < -joystick_keymap.dead_zone)
418 case SDL_JOYBUTTONDOWN:
419 if (event.jbutton.button == joystick_keymap.a_button)
421 else if (event.jbutton.button == joystick_keymap.b_button)
422 tux.input.fire = DOWN;
423 else if (event.jbutton.button == joystick_keymap.start_button)
426 case SDL_JOYBUTTONUP:
427 if (event.jbutton.button == joystick_keymap.a_button)
429 else if (event.jbutton.button == joystick_keymap.b_button)
442 GameSession::check_end_conditions()
444 Player* tux = currentsector->player;
447 Tile* endtile = collision_goal(tux->base);
449 if(end_sequence && !endsequence_timer.check())
451 exit_status = ES_LEVEL_FINISHED;
454 else if(end_sequence == ENDSEQUENCE_RUNNING && endtile && endtile->data >= 1)
456 end_sequence = ENDSEQUENCE_WAITING;
458 else if(!end_sequence && endtile && endtile->data == 0)
460 end_sequence = ENDSEQUENCE_RUNNING;
462 sound_manager->play_music(level_end_song, 0);
463 endsequence_timer.start(7000); // 5 seconds until we finish the map
464 tux->invincible_timer.start(7000); //FIXME: Implement a winning timer for the end sequence (with special winning animation etc.)
466 else if (!end_sequence && tux->is_dead())
468 player_status.bonus = PlayerStatus::NO_BONUS;
470 if (player_status.lives < 0)
472 exit_status = ES_GAME_OVER;
475 { // Still has lives, so reset Tux to the levelstart
484 GameSession::action(double frame_ratio)
486 if (exit_status == ES_NONE && !currentsector->player->growing_timer.check())
488 // Update Tux and the World
489 currentsector->action(frame_ratio);
492 // respawning in new sector?
493 if(newsector != "" && newspawnpoint != "") {
494 Sector* sector = level->get_sector(newsector);
495 currentsector = sector;
496 currentsector->activate(newspawnpoint);
497 currentsector->play_music(LEVEL_MUSIC);
498 newsector = newspawnpoint = "";
505 currentsector->draw(*context);
506 drawstatus(*context);
510 int x = screen->h / 20;
511 for(int i = 0; i < x; ++i)
513 context->draw_filled_rect(
514 Vector(i % 2 ? (pause_menu_frame * i)%screen->w :
515 -((pause_menu_frame * i)%screen->w)
516 ,(i*20+pause_menu_frame)%screen->h),
517 Vector(screen->w,10),
518 Color(20,20,20, rand() % 20 + 1), LAYER_FOREGROUND1+1);
520 context->draw_filled_rect(
521 Vector(0,0), Vector(screen->w, screen->h),
522 Color(rand() % 50, rand() % 50, rand() % 50, 128), LAYER_FOREGROUND1);
523 context->draw_text_center(blue_text, _("PAUSE - Press 'P' To Play"),
524 Vector(0, 230), LAYER_FOREGROUND1+2);
529 Menu::current()->draw(*context);
530 mouse_cursor->draw(*context);
533 context->do_drawing();
537 GameSession::process_menu()
539 Menu* menu = Menu::current();
544 if(menu == game_menu)
546 switch (game_menu->check())
549 st_pause_ticks_stop();
551 case MNID_ABORTLEVEL:
552 st_pause_ticks_stop();
553 exit_status = ES_LEVEL_ABORT;
557 else if(menu == options_menu)
559 process_options_menu();
561 else if(menu == load_game_menu )
563 process_load_game_menu();
568 GameSession::ExitStatus
571 Menu::set_current(0);
576 update_time = last_update_time = st_get_ticks();
578 // Eat unneeded events
580 while (SDL_PollEvent(&event)) {}
584 while (exit_status == ES_NONE)
586 /* Calculate the movement-factor */
587 double frame_ratio = ((double)(update_time-last_update_time))/((double)FRAME_RATE);
589 if(!frame_timer.check())
591 frame_timer.start(25);
592 ++global_frame_counter;
596 currentsector->player->input.old_fire
597 = currentsector->player->input.fire;
602 // Update the world state and all objects in the world
603 // Do that with a constante time-delta so that the game will run
604 // determistic and not different on different machines
605 if(!game_pause && !Menu::current())
608 check_end_conditions();
609 if (end_sequence == ENDSEQUENCE_RUNNING)
610 action(frame_ratio/2);
611 else if(end_sequence == NO_ENDSEQUENCE)
622 /* Time stops in pause mode */
623 if(game_pause || Menu::current())
628 /* Set the time of the last update and the time of the current update */
629 last_update_time = update_time;
630 update_time = st_get_ticks();
632 /* Pause till next frame, if the machine running the game is too fast: */
633 /* FIXME: Works great for in OpenGl mode, where the CPU doesn't have to do that much. But
634 the results in SDL mode aren't perfect (thought the 100 FPS are reached), even on an AMD2500+. */
635 if(last_update_time >= update_time - 12)
638 update_time = st_get_ticks();
642 if (!time_left.check() && currentsector->player->dying == DYING_NOT
644 currentsector->player->kill(Player::KILL);
647 if(currentsector->player->invincible_timer.check() && !end_sequence)
649 currentsector->play_music(HERRING_MUSIC);
651 /* are we low on time ? */
652 else if (time_left.get_left() < TIME_WARNING && !end_sequence)
654 currentsector->play_music(HURRYUP_MUSIC);
656 /* or just normal music? */
657 else if(currentsector->get_music_type() != LEVEL_MUSIC && !end_sequence)
659 currentsector->play_music(LEVEL_MUSIC);
662 /* Calculate frames per second */
666 fps_fps = (1000.0 / (float)fps_timer.get_gone()) * (float)fps_cnt;
668 if(!fps_timer.check())
670 fps_timer.start(1000);
680 GameSession::respawn(const std::string& sector, const std::string& spawnpoint)
683 newspawnpoint = spawnpoint;
686 /* Bounce a brick: */
687 void bumpbrick(float x, float y)
689 Sector::current()->add_bouncy_brick(Vector(((int)(x + 1) / 32) * 32,
690 (int)(y / 32) * 32));
692 sound_manager->play_sound(sounds[SND_BRICK], Vector(x, y));
697 GameSession::drawstatus(DrawingContext& context)
701 snprintf(str, 60, " %d", player_status.score);
702 context.draw_text(white_text, _("SCORE"), Vector(0, 0), LAYER_FOREGROUND1);
703 context.draw_text(gold_text, str, Vector(96, 0), LAYER_FOREGROUND1);
705 if(st_gl_mode == ST_GL_TEST)
707 context.draw_text(white_text, _("Press ESC To Return"), Vector(0,20),
711 if(!time_left.check()) {
712 context.draw_text_center(white_text, _("TIME's UP"), Vector(0, 0),
714 } else if (time_left.get_left() > TIME_WARNING || (global_frame_counter % 10) < 5) {
715 sprintf(str, " %d", time_left.get_left() / 1000 );
716 context.draw_text_center(white_text, _("TIME"),
717 Vector(0, 0), LAYER_FOREGROUND1);
718 context.draw_text_center(gold_text, str,
719 Vector(4*16, 0), LAYER_FOREGROUND1);
722 sprintf(str, " %d", player_status.distros);
723 context.draw_text(white_text, _("COINS"),
724 Vector(screen->w - white_text->get_text_width(_("COINS"))-white_text->get_text_width(str), 0),
726 context.draw_text(gold_text, str,
727 Vector(screen->w - gold_text->get_text_width(" 99"), 0),LAYER_FOREGROUND1);
729 if (player_status.lives >= 5)
731 sprintf(str, "%dx", player_status.lives);
732 float x = screen->w - gold_text->get_text_width(str) - tux_life->w;
733 context.draw_text(gold_text, str, Vector(x, 20), LAYER_FOREGROUND1);
734 context.draw_surface(tux_life, Vector(screen->w - 16, 20),
739 for(int i= 0; i < player_status.lives; ++i)
740 context.draw_surface(tux_life,
741 Vector(screen->w - tux_life->w*4 +(tux_life->w*i), 20),
745 context.draw_text(white_text, _("LIVES"),
746 Vector(screen->w - white_text->get_text_width(_("LIVES")) - white_text->get_text_width(" 99"), 20),
751 sprintf(str, "%2.1f", fps_fps);
752 context.draw_text(white_text, "FPS",
753 Vector(screen->w - white_text->get_text_width("FPS "), 40),
755 context.draw_text(gold_text, str,
756 Vector(screen->w-4*16, 40), LAYER_FOREGROUND1);
761 GameSession::drawresultscreen(void)
765 DrawingContext context;
766 currentsector->background->draw(context);
768 context.draw_text_center(blue_text, _("Result:"), Vector(0, 200),
771 sprintf(str, _("SCORE: %d"), player_status.score);
772 context.draw_text_center(gold_text, str, Vector(0, 224), LAYER_FOREGROUND1);
774 sprintf(str, _("COINS: %d"), player_status.distros);
775 context.draw_text_center(gold_text, str, Vector(0, 256), LAYER_FOREGROUND1);
777 context.do_drawing();
780 wait_for_event(event,2000,5000,true);
783 std::string slotinfo(int slot)
788 sprintf(slotfile,"%s/slot%d.stsg",st_save_dir,slot);
790 lisp_object_t* savegame = lisp_read_from_file(slotfile);
793 LispReader reader(lisp_cdr(savegame));
794 reader.read_string("title", title);
798 if (access(slotfile, F_OK) == 0)
801 snprintf(tmp,1024,"Slot %d - %s",slot, title.c_str());
803 snprintf(tmp, 1024,_("Slot %d - Savegame"),slot);
806 sprintf(tmp,_("Slot %d - Free"),slot);