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);
528 sprintf(str1, _("Playing: "));
529 sprintf(str2, level->name.c_str());
531 context->draw_text(blue_text, str1,
532 Vector((screen->w - (blue_text->get_text_width(str1) + white_text->get_text_width(str2)))/2, 340),
533 LAYER_FOREGROUND1+2);
534 context->draw_text(white_text, str2,
535 Vector(((screen->w - (blue_text->get_text_width(str1) + white_text->get_text_width(str2)))/2)+blue_text->get_text_width(str1), 340),
536 LAYER_FOREGROUND1+2);
541 Menu::current()->draw(*context);
542 mouse_cursor->draw(*context);
545 context->do_drawing();
549 GameSession::process_menu()
551 Menu* menu = Menu::current();
556 if(menu == game_menu)
558 switch (game_menu->check())
561 st_pause_ticks_stop();
563 case MNID_ABORTLEVEL:
564 st_pause_ticks_stop();
565 exit_status = ES_LEVEL_ABORT;
569 else if(menu == options_menu)
571 process_options_menu();
573 else if(menu == load_game_menu )
575 process_load_game_menu();
580 GameSession::ExitStatus
583 Menu::set_current(0);
588 update_time = last_update_time = st_get_ticks();
590 // Eat unneeded events
592 while (SDL_PollEvent(&event)) {}
596 while (exit_status == ES_NONE)
598 /* Calculate the movement-factor */
599 double frame_ratio = ((double)(update_time-last_update_time))/((double)FRAME_RATE);
601 if(!frame_timer.check())
603 frame_timer.start(25);
604 ++global_frame_counter;
608 currentsector->player->input.old_fire
609 = currentsector->player->input.fire;
614 // Update the world state and all objects in the world
615 // Do that with a constante time-delta so that the game will run
616 // determistic and not different on different machines
617 if(!game_pause && !Menu::current())
620 check_end_conditions();
621 if (end_sequence == ENDSEQUENCE_RUNNING)
622 action(frame_ratio/2);
623 else if(end_sequence == NO_ENDSEQUENCE)
634 /* Time stops in pause mode */
635 if(game_pause || Menu::current())
640 /* Set the time of the last update and the time of the current update */
641 last_update_time = update_time;
642 update_time = st_get_ticks();
644 /* Pause till next frame, if the machine running the game is too fast: */
645 /* FIXME: Works great for in OpenGl mode, where the CPU doesn't have to do that much. But
646 the results in SDL mode aren't perfect (thought the 100 FPS are reached), even on an AMD2500+. */
647 if(last_update_time >= update_time - 12)
650 update_time = st_get_ticks();
654 if (!time_left.check() && currentsector->player->dying == DYING_NOT
656 currentsector->player->kill(Player::KILL);
659 if(currentsector->player->invincible_timer.check() && !end_sequence)
661 currentsector->play_music(HERRING_MUSIC);
663 /* are we low on time ? */
664 else if (time_left.get_left() < TIME_WARNING && !end_sequence)
666 currentsector->play_music(HURRYUP_MUSIC);
668 /* or just normal music? */
669 else if(currentsector->get_music_type() != LEVEL_MUSIC && !end_sequence)
671 currentsector->play_music(LEVEL_MUSIC);
674 /* Calculate frames per second */
678 fps_fps = (1000.0 / (float)fps_timer.get_gone()) * (float)fps_cnt;
680 if(!fps_timer.check())
682 fps_timer.start(1000);
692 GameSession::respawn(const std::string& sector, const std::string& spawnpoint)
695 newspawnpoint = spawnpoint;
698 /* Bounce a brick: */
699 void bumpbrick(float x, float y)
701 Sector::current()->add_bouncy_brick(Vector(((int)(x + 1) / 32) * 32,
702 (int)(y / 32) * 32));
704 sound_manager->play_sound(sounds[SND_BRICK], Vector(x, y));
709 GameSession::drawstatus(DrawingContext& context)
713 snprintf(str, 60, " %d", player_status.score);
714 context.draw_text(white_text, _("SCORE"), Vector(0, 0), LAYER_FOREGROUND1);
715 context.draw_text(gold_text, str, Vector(96, 0), LAYER_FOREGROUND1);
717 if(st_gl_mode == ST_GL_TEST)
719 context.draw_text(white_text, _("Press ESC To Return"), Vector(0,20),
723 if(!time_left.check()) {
724 context.draw_text_center(white_text, _("TIME's UP"), Vector(0, 0),
726 } else if (time_left.get_left() > TIME_WARNING || (global_frame_counter % 10) < 5) {
727 sprintf(str, " %d", time_left.get_left() / 1000 );
728 context.draw_text_center(white_text, _("TIME"),
729 Vector(0, 0), LAYER_FOREGROUND1);
730 context.draw_text_center(gold_text, str,
731 Vector(4*16, 0), LAYER_FOREGROUND1);
734 sprintf(str, " %d", player_status.distros);
735 context.draw_text(white_text, _("COINS"),
736 Vector(screen->w - white_text->get_text_width(_("COINS"))-white_text->get_text_width(" 99"), 0),
738 context.draw_text(gold_text, str,
739 Vector(screen->w - gold_text->get_text_width(" 99"), 0),LAYER_FOREGROUND1);
741 if (player_status.lives >= 5)
743 sprintf(str, "%dx", player_status.lives);
744 float x = screen->w - gold_text->get_text_width(str) - tux_life->w;
745 context.draw_text(gold_text, str, Vector(x, 20), LAYER_FOREGROUND1);
746 context.draw_surface(tux_life, Vector(screen->w - 16, 20),
751 for(int i= 0; i < player_status.lives; ++i)
752 context.draw_surface(tux_life,
753 Vector(screen->w - tux_life->w*4 +(tux_life->w*i), 20),
757 context.draw_text(white_text, _("LIVES"),
758 Vector(screen->w - white_text->get_text_width(_("LIVES")) - white_text->get_text_width(" 99"), 20),
763 sprintf(str, "%2.1f", fps_fps);
764 context.draw_text(white_text, "FPS",
765 Vector(screen->w - white_text->get_text_width("FPS "), 40),
767 context.draw_text(gold_text, str,
768 Vector(screen->w-4*16, 40), LAYER_FOREGROUND1);
773 GameSession::drawresultscreen(void)
777 DrawingContext context;
778 currentsector->background->draw(context);
780 context.draw_text_center(blue_text, _("Result:"), Vector(0, 200),
783 sprintf(str, _("SCORE: %d"), player_status.score);
784 context.draw_text_center(gold_text, str, Vector(0, 224), LAYER_FOREGROUND1);
786 sprintf(str, _("COINS: %d"), player_status.distros);
787 context.draw_text_center(gold_text, str, Vector(0, 256), LAYER_FOREGROUND1);
789 context.do_drawing();
792 wait_for_event(event,2000,5000,true);
795 std::string slotinfo(int slot)
800 sprintf(slotfile,"%s/slot%d.stsg",st_save_dir,slot);
802 lisp_object_t* savegame = lisp_read_from_file(slotfile);
805 LispReader reader(lisp_cdr(savegame));
806 reader.read_string("title", title);
810 if (access(slotfile, F_OK) == 0)
813 snprintf(tmp,1024,"Slot %d - %s",slot, title.c_str());
815 snprintf(tmp, 1024,_("Slot %d - Savegame"),slot);
818 sprintf(tmp,_("Slot %d - Free"),slot);