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, bool flip_level_)
63 : level(0), currentsector(0), st_gl_mode(mode),
64 end_sequence(NO_ENDSEQUENCE), levelname(levelname_), flip_level(flip_level_)
68 global_frame_counter = 0;
73 frame_timer.init(true);
75 context = new DrawingContext();
84 GameSession::restart_level()
87 exit_status = ES_NONE;
88 end_sequence = NO_ENDSEQUENCE;
91 frame_timer.init(true);
96 { // Tux has lost a life, so we try to respawn him at the nearest reset point
97 old_x_pos = world->get_tux()->base.x;
105 level->load(levelname);
107 level->do_vertical_flip();
108 currentsector = level->get_sector("main");
110 st_abort("Level has no main sector.", "");
111 currentsector->activate("main");
114 // Set Tux to the nearest reset point
117 ResetPoint best_reset_point = { -1, -1 };
118 for(std::vector<ResetPoint>::iterator i = get_level()->reset_points.begin();
119 i != get_level()->reset_points.end(); ++i)
121 if (i->x < old_x_pos && best_reset_point.x < i->x)
122 best_reset_point = *i;
125 if (best_reset_point.x != -1)
127 world->get_tux()->base.x = best_reset_point.x;
128 world->get_tux()->base.y = best_reset_point.y;
133 if (st_gl_mode != ST_GL_DEMO_GAME)
135 if(st_gl_mode == ST_GL_PLAY || st_gl_mode == ST_GL_LOAD_LEVEL_FILE)
139 time_left.init(true);
141 currentsector->play_music(LEVEL_MUSIC);
144 GameSession::~GameSession()
151 GameSession::levelintro(void)
153 sound_manager->halt_music();
157 DrawingContext context;
158 currentsector->background->draw(context);
160 context.draw_text_center(gold_text, level->get_name(), Vector(0, 220),
163 sprintf(str, "TUX x %d", player_status.lives);
164 context.draw_text_center(white_text, str, Vector(0, 240),
167 if(level->get_author().size())
168 context.draw_text_center(white_small_text,
169 std::string(_("by ")) + level->get_author(),
170 Vector(0, 400), LAYER_FOREGROUND1);
174 context.draw_text_center(white_text,
175 _("Level Vertically Flipped!"),
176 Vector(0, 310), LAYER_FOREGROUND1);
178 context.do_drawing();
181 wait_for_event(event,1000,3000,true);
186 GameSession::start_timers()
188 time_left.start(level->time_left*1000);
189 st_pause_ticks_init();
190 update_time = st_get_ticks();
194 GameSession::on_escape_press()
196 if(currentsector->player->dying || end_sequence != NO_ENDSEQUENCE)
197 return; // don't let the player open the menu, when he is dying
202 if(st_gl_mode == ST_GL_TEST)
204 exit_status = ES_LEVEL_ABORT;
206 else if (!Menu::current())
208 /* Tell Tux that the keys are all down, otherwise
209 it could have nasty bugs, like going allways to the right
210 or whatever that key does */
211 Player& tux = *(currentsector->player);
212 tux.key_event((SDLKey)keymap.jump, UP);
213 tux.key_event((SDLKey)keymap.duck, UP);
214 tux.key_event((SDLKey)keymap.left, UP);
215 tux.key_event((SDLKey)keymap.right, UP);
216 tux.key_event((SDLKey)keymap.fire, UP);
218 Menu::set_current(game_menu);
219 st_pause_ticks_start();
224 GameSession::process_events()
226 if (end_sequence != NO_ENDSEQUENCE)
228 Player& tux = *currentsector->player;
232 tux.input.right = DOWN;
235 if (int(last_x_pos) == int(tux.base.x))
240 last_x_pos = tux.base.x;
243 while (SDL_PollEvent(&event))
245 /* Check for menu-events, if the menu is shown */
248 Menu::current()->event(event);
250 st_pause_ticks_stop();
255 case SDL_QUIT: /* Quit event - quit: */
256 st_abort("Received window close", "");
259 case SDL_KEYDOWN: /* A keypress! */
261 SDLKey key = event.key.keysym.sym;
265 case SDLK_ESCAPE: /* Escape: Open/Close the menu: */
273 case SDL_JOYBUTTONDOWN:
274 if (event.jbutton.button == joystick_keymap.start_button)
282 if(!Menu::current() && !game_pause)
283 st_pause_ticks_stop();
286 while (SDL_PollEvent(&event))
288 /* Check for menu-events, if the menu is shown */
291 Menu::current()->event(event);
293 st_pause_ticks_stop();
297 Player& tux = *currentsector->player;
301 case SDL_QUIT: /* Quit event - quit: */
302 st_abort("Received window close", "");
305 case SDL_KEYDOWN: /* A keypress! */
307 SDLKey key = event.key.keysym.sym;
309 if(tux.key_event(key,DOWN))
314 case SDLK_ESCAPE: /* Escape: Open/Close the menu: */
322 case SDL_KEYUP: /* A keyrelease! */
324 SDLKey key = event.key.keysym.sym;
326 if(tux.key_event(key, UP))
335 snprintf(buf, sizeof(buf), "P: %4.1f,%4.1f",
336 tux.base.x, tux.base.y);
337 context->draw_text(white_text, buf,
338 Vector(0, screen->h - white_text->get_height()),
340 context->do_drawing();
350 st_pause_ticks_stop();
355 st_pause_ticks_start();
367 player_status.distros += 50;
371 tux.got_power = tux.FIRE_POWER;
375 tux.got_power = tux.ICE_POWER;
379 tux.invincible_timer.start(TUX_INVINCIBLE_TIME);
383 --player_status.lives;
387 player_status.score += 1000;
400 case SDL_JOYAXISMOTION:
401 if (event.jaxis.axis == joystick_keymap.x_axis)
403 if (event.jaxis.value < -joystick_keymap.dead_zone)
405 tux.input.left = DOWN;
406 tux.input.right = UP;
408 else if (event.jaxis.value > joystick_keymap.dead_zone)
411 tux.input.right = DOWN;
415 tux.input.left = DOWN;
416 tux.input.right = DOWN;
419 else if (event.jaxis.axis == joystick_keymap.y_axis)
421 if (event.jaxis.value > joystick_keymap.dead_zone)
422 tux.input.down = DOWN;
423 else if (event.jaxis.value < -joystick_keymap.dead_zone)
430 case SDL_JOYBUTTONDOWN:
431 if (event.jbutton.button == joystick_keymap.a_button)
433 else if (event.jbutton.button == joystick_keymap.b_button)
434 tux.input.fire = DOWN;
435 else if (event.jbutton.button == joystick_keymap.start_button)
438 case SDL_JOYBUTTONUP:
439 if (event.jbutton.button == joystick_keymap.a_button)
441 else if (event.jbutton.button == joystick_keymap.b_button)
454 GameSession::check_end_conditions()
456 Player* tux = currentsector->player;
459 Tile* endtile = collision_goal(tux->base);
461 if(end_sequence && !endsequence_timer.check())
463 exit_status = ES_LEVEL_FINISHED;
466 else if(end_sequence == ENDSEQUENCE_RUNNING && endtile && endtile->data >= 1)
468 end_sequence = ENDSEQUENCE_WAITING;
470 else if(!end_sequence && endtile && endtile->data == 0)
472 end_sequence = ENDSEQUENCE_RUNNING;
474 sound_manager->play_music(level_end_song, 0);
475 endsequence_timer.start(7000); // 5 seconds until we finish the map
476 tux->invincible_timer.start(7000); //FIXME: Implement a winning timer for the end sequence (with special winning animation etc.)
478 else if (!end_sequence && tux->is_dead())
480 player_status.bonus = PlayerStatus::NO_BONUS;
482 if (player_status.lives < 0)
484 exit_status = ES_GAME_OVER;
487 { // Still has lives, so reset Tux to the levelstart
496 GameSession::action(double frame_ratio)
498 if (exit_status == ES_NONE && !currentsector->player->growing_timer.check())
500 // Update Tux and the World
501 currentsector->action(frame_ratio);
504 // respawning in new sector?
505 if(newsector != "" && newspawnpoint != "") {
506 Sector* sector = level->get_sector(newsector);
507 currentsector = sector;
508 currentsector->activate(newspawnpoint);
509 currentsector->play_music(LEVEL_MUSIC);
510 newsector = newspawnpoint = "";
517 currentsector->draw(*context);
518 drawstatus(*context);
522 int x = screen->h / 20;
523 for(int i = 0; i < x; ++i)
525 context->draw_filled_rect(
526 Vector(i % 2 ? (pause_menu_frame * i)%screen->w :
527 -((pause_menu_frame * i)%screen->w)
528 ,(i*20+pause_menu_frame)%screen->h),
529 Vector(screen->w,10),
530 Color(20,20,20, rand() % 20 + 1), LAYER_FOREGROUND1+1);
532 context->draw_filled_rect(
533 Vector(0,0), Vector(screen->w, screen->h),
534 Color(rand() % 50, rand() % 50, rand() % 50, 128), LAYER_FOREGROUND1);
535 context->draw_text_center(blue_text, _("PAUSE - Press 'P' To Play"),
536 Vector(0, 230), LAYER_FOREGROUND1+2);
540 sprintf(str1, _("Playing: "));
541 sprintf(str2, level->name.c_str());
543 context->draw_text(blue_text, str1,
544 Vector((screen->w - (blue_text->get_text_width(str1) + white_text->get_text_width(str2)))/2, 340),
545 LAYER_FOREGROUND1+2);
546 context->draw_text(white_text, str2,
547 Vector(((screen->w - (blue_text->get_text_width(str1) + white_text->get_text_width(str2)))/2)+blue_text->get_text_width(str1), 340),
548 LAYER_FOREGROUND1+2);
553 Menu::current()->draw(*context);
554 mouse_cursor->draw(*context);
557 context->do_drawing();
561 GameSession::process_menu()
563 Menu* menu = Menu::current();
568 if(menu == game_menu)
570 switch (game_menu->check())
573 st_pause_ticks_stop();
575 case MNID_ABORTLEVEL:
576 st_pause_ticks_stop();
577 exit_status = ES_LEVEL_ABORT;
581 else if(menu == options_menu)
583 process_options_menu();
585 else if(menu == load_game_menu )
587 process_load_game_menu();
592 GameSession::ExitStatus
595 Menu::set_current(0);
600 update_time = last_update_time = st_get_ticks();
602 // Eat unneeded events
604 while (SDL_PollEvent(&event)) {}
608 while (exit_status == ES_NONE)
610 /* Calculate the movement-factor */
611 double frame_ratio = ((double)(update_time-last_update_time))/((double)FRAME_RATE);
613 if(!frame_timer.check())
615 frame_timer.start(25);
616 ++global_frame_counter;
620 currentsector->player->input.old_fire
621 = currentsector->player->input.fire;
626 // Update the world state and all objects in the world
627 // Do that with a constante time-delta so that the game will run
628 // determistic and not different on different machines
629 if(!game_pause && !Menu::current())
632 check_end_conditions();
633 if (end_sequence == ENDSEQUENCE_RUNNING)
634 action(frame_ratio/2);
635 else if(end_sequence == NO_ENDSEQUENCE)
646 /* Time stops in pause mode */
647 if(game_pause || Menu::current())
652 /* Set the time of the last update and the time of the current update */
653 last_update_time = update_time;
654 update_time = st_get_ticks();
656 /* Pause till next frame, if the machine running the game is too fast: */
657 /* FIXME: Works great for in OpenGl mode, where the CPU doesn't have to do that much. But
658 the results in SDL mode aren't perfect (thought the 100 FPS are reached), even on an AMD2500+. */
659 if(last_update_time >= update_time - 12)
662 update_time = st_get_ticks();
666 if (!time_left.check() && currentsector->player->dying == DYING_NOT
668 currentsector->player->kill(Player::KILL);
671 if(currentsector->player->invincible_timer.check() && !end_sequence)
673 currentsector->play_music(HERRING_MUSIC);
675 /* are we low on time ? */
676 else if (time_left.get_left() < TIME_WARNING && !end_sequence)
678 currentsector->play_music(HURRYUP_MUSIC);
680 /* or just normal music? */
681 else if(currentsector->get_music_type() != LEVEL_MUSIC && !end_sequence)
683 currentsector->play_music(LEVEL_MUSIC);
686 /* Calculate frames per second */
690 fps_fps = (1000.0 / (float)fps_timer.get_gone()) * (float)fps_cnt;
692 if(!fps_timer.check())
694 fps_timer.start(1000);
704 GameSession::respawn(const std::string& sector, const std::string& spawnpoint)
707 newspawnpoint = spawnpoint;
710 /* Bounce a brick: */
711 void bumpbrick(float x, float y)
713 Sector::current()->add_bouncy_brick(Vector(((int)(x + 1) / 32) * 32,
714 (int)(y / 32) * 32));
716 sound_manager->play_sound(sounds[SND_BRICK], Vector(x, y));
721 GameSession::drawstatus(DrawingContext& context)
725 snprintf(str, 60, " %d", player_status.score);
726 context.draw_text(white_text, _("SCORE"), Vector(0, 0), LAYER_FOREGROUND1);
727 context.draw_text(gold_text, str, Vector(96, 0), LAYER_FOREGROUND1);
729 if(st_gl_mode == ST_GL_TEST)
731 context.draw_text(white_text, _("Press ESC To Return"), Vector(0,20),
735 if(!time_left.check()) {
736 context.draw_text_center(white_text, _("TIME's UP"), Vector(0, 0),
738 } else if (time_left.get_left() > TIME_WARNING || (global_frame_counter % 10) < 5) {
739 sprintf(str, " %d", time_left.get_left() / 1000 );
740 context.draw_text_center(white_text, _("TIME"),
741 Vector(0, 0), LAYER_FOREGROUND1);
742 context.draw_text_center(gold_text, str,
743 Vector(4*16, 0), LAYER_FOREGROUND1);
746 sprintf(str, " %d", player_status.distros);
747 context.draw_text(white_text, _("COINS"),
748 Vector(screen->w - white_text->get_text_width(_("COINS"))-white_text->get_text_width(" 99"), 0),
750 context.draw_text(gold_text, str,
751 Vector(screen->w - gold_text->get_text_width(" 99"), 0),LAYER_FOREGROUND1);
753 if (player_status.lives >= 5)
755 sprintf(str, "%dx", player_status.lives);
756 float x = screen->w - gold_text->get_text_width(str) - tux_life->w;
757 context.draw_text(gold_text, str, Vector(x, 20), LAYER_FOREGROUND1);
758 context.draw_surface(tux_life, Vector(screen->w - 16, 20),
763 for(int i= 0; i < player_status.lives; ++i)
764 context.draw_surface(tux_life,
765 Vector(screen->w - tux_life->w*4 +(tux_life->w*i), 20),
769 context.draw_text(white_text, _("LIVES"),
770 Vector(screen->w - white_text->get_text_width(_("LIVES")) - white_text->get_text_width(" 99"), 20),
775 sprintf(str, "%2.1f", fps_fps);
776 context.draw_text(white_text, "FPS",
777 Vector(screen->w - white_text->get_text_width("FPS "), 40),
779 context.draw_text(gold_text, str,
780 Vector(screen->w-4*16, 40), LAYER_FOREGROUND1);
785 GameSession::drawresultscreen(void)
789 DrawingContext context;
790 currentsector->background->draw(context);
792 context.draw_text_center(blue_text, _("Result:"), Vector(0, 200),
795 sprintf(str, _("SCORE: %d"), player_status.score);
796 context.draw_text_center(gold_text, str, Vector(0, 224), LAYER_FOREGROUND1);
798 sprintf(str, _("COINS: %d"), player_status.distros);
799 context.draw_text_center(gold_text, str, Vector(0, 256), LAYER_FOREGROUND1);
801 context.do_drawing();
804 wait_for_event(event,2000,5000,true);
807 std::string slotinfo(int slot)
812 sprintf(slotfile,"%s/slot%d.stsg",st_save_dir,slot);
814 lisp_object_t* savegame = lisp_read_from_file(slotfile);
817 LispReader reader(lisp_cdr(savegame));
818 reader.read_string("title", title);
822 if (access(slotfile, F_OK) == 0)
825 snprintf(tmp,1024,"Slot %d - %s",slot, title.c_str());
827 snprintf(tmp, 1024,_("Slot %d - Savegame"),slot);
830 sprintf(tmp,_("Slot %d - Free"),slot);