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 if(level->get_author().size())
163 context.draw_text_center(white_small_text,
164 std::string(_("by ")) + level->get_author(),
165 Vector(0, 400), LAYER_FOREGROUND1);
167 context.do_drawing();
170 wait_for_event(event,1000,3000,true);
175 GameSession::start_timers()
177 time_left.start(level->time_left*1000);
178 st_pause_ticks_init();
179 update_time = st_get_ticks();
183 GameSession::on_escape_press()
185 if(currentsector->player->dying || end_sequence != NO_ENDSEQUENCE)
186 return; // don't let the player open the menu, when he is dying
191 if(st_gl_mode == ST_GL_TEST)
193 exit_status = ES_LEVEL_ABORT;
195 else if (!Menu::current())
197 /* Tell Tux that the keys are all down, otherwise
198 it could have nasty bugs, like going allways to the right
199 or whatever that key does */
200 Player& tux = *(currentsector->player);
201 tux.key_event((SDLKey)keymap.jump, UP);
202 tux.key_event((SDLKey)keymap.duck, UP);
203 tux.key_event((SDLKey)keymap.left, UP);
204 tux.key_event((SDLKey)keymap.right, UP);
205 tux.key_event((SDLKey)keymap.fire, UP);
207 Menu::set_current(game_menu);
208 st_pause_ticks_start();
213 GameSession::process_events()
215 if (end_sequence != NO_ENDSEQUENCE)
217 Player& tux = *currentsector->player;
221 tux.input.right = DOWN;
224 if (int(last_x_pos) == int(tux.base.x))
229 last_x_pos = tux.base.x;
232 while (SDL_PollEvent(&event))
234 /* Check for menu-events, if the menu is shown */
237 Menu::current()->event(event);
239 st_pause_ticks_stop();
244 case SDL_QUIT: /* Quit event - quit: */
245 st_abort("Received window close", "");
248 case SDL_KEYDOWN: /* A keypress! */
250 SDLKey key = event.key.keysym.sym;
254 case SDLK_ESCAPE: /* Escape: Open/Close the menu: */
262 case SDL_JOYBUTTONDOWN:
263 if (event.jbutton.button == joystick_keymap.start_button)
271 if(!Menu::current() && !game_pause)
272 st_pause_ticks_stop();
275 while (SDL_PollEvent(&event))
277 /* Check for menu-events, if the menu is shown */
280 Menu::current()->event(event);
282 st_pause_ticks_stop();
286 Player& tux = *currentsector->player;
290 case SDL_QUIT: /* Quit event - quit: */
291 st_abort("Received window close", "");
294 case SDL_KEYDOWN: /* A keypress! */
296 SDLKey key = event.key.keysym.sym;
298 if(tux.key_event(key,DOWN))
303 case SDLK_ESCAPE: /* Escape: Open/Close the menu: */
311 case SDL_KEYUP: /* A keyrelease! */
313 SDLKey key = event.key.keysym.sym;
315 if(tux.key_event(key, UP))
324 snprintf(buf, sizeof(buf), "P: %4.1f,%4.1f",
325 tux.base.x, tux.base.y);
326 context->draw_text(white_text, buf,
327 Vector(0, screen->h - white_text->get_height()),
329 context->do_drawing();
339 st_pause_ticks_stop();
344 st_pause_ticks_start();
356 player_status.distros += 50;
360 tux.got_power = tux.FIRE_POWER;
364 tux.got_power = tux.ICE_POWER;
368 tux.invincible_timer.start(TUX_INVINCIBLE_TIME);
372 --player_status.lives;
376 player_status.score += 1000;
389 case SDL_JOYAXISMOTION:
390 if (event.jaxis.axis == joystick_keymap.x_axis)
392 if (event.jaxis.value < -joystick_keymap.dead_zone)
394 tux.input.left = DOWN;
395 tux.input.right = UP;
397 else if (event.jaxis.value > joystick_keymap.dead_zone)
400 tux.input.right = DOWN;
404 tux.input.left = DOWN;
405 tux.input.right = DOWN;
408 else if (event.jaxis.axis == joystick_keymap.y_axis)
410 if (event.jaxis.value > joystick_keymap.dead_zone)
411 tux.input.down = DOWN;
412 else if (event.jaxis.value < -joystick_keymap.dead_zone)
419 case SDL_JOYBUTTONDOWN:
420 if (event.jbutton.button == joystick_keymap.a_button)
422 else if (event.jbutton.button == joystick_keymap.b_button)
423 tux.input.fire = DOWN;
424 else if (event.jbutton.button == joystick_keymap.start_button)
427 case SDL_JOYBUTTONUP:
428 if (event.jbutton.button == joystick_keymap.a_button)
430 else if (event.jbutton.button == joystick_keymap.b_button)
443 GameSession::check_end_conditions()
445 Player* tux = currentsector->player;
448 Tile* endtile = collision_goal(tux->base);
450 if(end_sequence && !endsequence_timer.check())
452 exit_status = ES_LEVEL_FINISHED;
455 else if(end_sequence == ENDSEQUENCE_RUNNING && endtile && endtile->data >= 1)
457 end_sequence = ENDSEQUENCE_WAITING;
459 else if(!end_sequence && endtile && endtile->data == 0)
461 end_sequence = ENDSEQUENCE_RUNNING;
463 sound_manager->play_music(level_end_song, 0);
464 endsequence_timer.start(7000); // 5 seconds until we finish the map
465 tux->invincible_timer.start(7000); //FIXME: Implement a winning timer for the end sequence (with special winning animation etc.)
467 else if (!end_sequence && tux->is_dead())
469 player_status.bonus = PlayerStatus::NO_BONUS;
471 if (player_status.lives < 0)
473 exit_status = ES_GAME_OVER;
476 { // Still has lives, so reset Tux to the levelstart
485 GameSession::action(double frame_ratio)
487 if (exit_status == ES_NONE && !currentsector->player->growing_timer.check())
489 // Update Tux and the World
490 currentsector->action(frame_ratio);
493 // respawning in new sector?
494 if(newsector != "" && newspawnpoint != "") {
495 Sector* sector = level->get_sector(newsector);
496 currentsector = sector;
497 currentsector->activate(newspawnpoint);
498 currentsector->play_music(LEVEL_MUSIC);
499 newsector = newspawnpoint = "";
506 currentsector->draw(*context);
507 drawstatus(*context);
511 int x = screen->h / 20;
512 for(int i = 0; i < x; ++i)
514 context->draw_filled_rect(
515 Vector(i % 2 ? (pause_menu_frame * i)%screen->w :
516 -((pause_menu_frame * i)%screen->w)
517 ,(i*20+pause_menu_frame)%screen->h),
518 Vector(screen->w,10),
519 Color(20,20,20, rand() % 20 + 1), LAYER_FOREGROUND1+1);
521 context->draw_filled_rect(
522 Vector(0,0), Vector(screen->w, screen->h),
523 Color(rand() % 50, rand() % 50, rand() % 50, 128), LAYER_FOREGROUND1);
524 context->draw_text_center(blue_text, _("PAUSE - Press 'P' To Play"),
525 Vector(0, 230), LAYER_FOREGROUND1+2);
529 sprintf(str1, _("Playing: "));
530 sprintf(str2, level->name.c_str());
532 context->draw_text(blue_text, str1,
533 Vector((screen->w - (blue_text->get_text_width(str1) + white_text->get_text_width(str2)))/2, 340),
534 LAYER_FOREGROUND1+2);
535 context->draw_text(white_text, str2,
536 Vector(((screen->w - (blue_text->get_text_width(str1) + white_text->get_text_width(str2)))/2)+blue_text->get_text_width(str1), 340),
537 LAYER_FOREGROUND1+2);
542 Menu::current()->draw(*context);
543 mouse_cursor->draw(*context);
546 context->do_drawing();
550 GameSession::process_menu()
552 Menu* menu = Menu::current();
557 if(menu == game_menu)
559 switch (game_menu->check())
562 st_pause_ticks_stop();
564 case MNID_ABORTLEVEL:
565 st_pause_ticks_stop();
566 exit_status = ES_LEVEL_ABORT;
570 else if(menu == options_menu)
572 process_options_menu();
574 else if(menu == load_game_menu )
576 process_load_game_menu();
581 GameSession::ExitStatus
584 Menu::set_current(0);
589 update_time = last_update_time = st_get_ticks();
591 // Eat unneeded events
593 while (SDL_PollEvent(&event)) {}
597 while (exit_status == ES_NONE)
599 /* Calculate the movement-factor */
600 double frame_ratio = ((double)(update_time-last_update_time))/((double)FRAME_RATE);
602 if(!frame_timer.check())
604 frame_timer.start(25);
605 ++global_frame_counter;
609 currentsector->player->input.old_fire
610 = currentsector->player->input.fire;
615 // Update the world state and all objects in the world
616 // Do that with a constante time-delta so that the game will run
617 // determistic and not different on different machines
618 if(!game_pause && !Menu::current())
621 check_end_conditions();
622 if (end_sequence == ENDSEQUENCE_RUNNING)
623 action(frame_ratio/2);
624 else if(end_sequence == NO_ENDSEQUENCE)
635 /* Time stops in pause mode */
636 if(game_pause || Menu::current())
641 /* Set the time of the last update and the time of the current update */
642 last_update_time = update_time;
643 update_time = st_get_ticks();
645 /* Pause till next frame, if the machine running the game is too fast: */
646 /* FIXME: Works great for in OpenGl mode, where the CPU doesn't have to do that much. But
647 the results in SDL mode aren't perfect (thought the 100 FPS are reached), even on an AMD2500+. */
648 if(last_update_time >= update_time - 12)
651 update_time = st_get_ticks();
655 if (!time_left.check() && currentsector->player->dying == DYING_NOT
657 currentsector->player->kill(Player::KILL);
660 if(currentsector->player->invincible_timer.check() && !end_sequence)
662 currentsector->play_music(HERRING_MUSIC);
664 /* are we low on time ? */
665 else if (time_left.get_left() < TIME_WARNING && !end_sequence)
667 currentsector->play_music(HURRYUP_MUSIC);
669 /* or just normal music? */
670 else if(currentsector->get_music_type() != LEVEL_MUSIC && !end_sequence)
672 currentsector->play_music(LEVEL_MUSIC);
675 /* Calculate frames per second */
679 fps_fps = (1000.0 / (float)fps_timer.get_gone()) * (float)fps_cnt;
681 if(!fps_timer.check())
683 fps_timer.start(1000);
693 GameSession::respawn(const std::string& sector, const std::string& spawnpoint)
696 newspawnpoint = spawnpoint;
699 /* Bounce a brick: */
700 void bumpbrick(float x, float y)
702 Sector::current()->add_bouncy_brick(Vector(((int)(x + 1) / 32) * 32,
703 (int)(y / 32) * 32));
705 sound_manager->play_sound(sounds[SND_BRICK], Vector(x, y));
710 GameSession::drawstatus(DrawingContext& context)
714 snprintf(str, 60, " %d", player_status.score);
715 context.draw_text(white_text, _("SCORE"), Vector(0, 0), LAYER_FOREGROUND1);
716 context.draw_text(gold_text, str, Vector(96, 0), LAYER_FOREGROUND1);
718 if(st_gl_mode == ST_GL_TEST)
720 context.draw_text(white_text, _("Press ESC To Return"), Vector(0,20),
724 if(!time_left.check()) {
725 context.draw_text_center(white_text, _("TIME's UP"), Vector(0, 0),
727 } else if (time_left.get_left() > TIME_WARNING || (global_frame_counter % 10) < 5) {
728 sprintf(str, " %d", time_left.get_left() / 1000 );
729 context.draw_text_center(white_text, _("TIME"),
730 Vector(0, 0), LAYER_FOREGROUND1);
731 context.draw_text_center(gold_text, str,
732 Vector(4*16, 0), LAYER_FOREGROUND1);
735 sprintf(str, " %d", player_status.distros);
736 context.draw_text(white_text, _("COINS"),
737 Vector(screen->w - white_text->get_text_width(_("COINS"))-white_text->get_text_width(" 99"), 0),
739 context.draw_text(gold_text, str,
740 Vector(screen->w - gold_text->get_text_width(" 99"), 0),LAYER_FOREGROUND1);
742 if (player_status.lives >= 5)
744 sprintf(str, "%dx", player_status.lives);
745 float x = screen->w - gold_text->get_text_width(str) - tux_life->w;
746 context.draw_text(gold_text, str, Vector(x, 20), LAYER_FOREGROUND1);
747 context.draw_surface(tux_life, Vector(screen->w - 16, 20),
752 for(int i= 0; i < player_status.lives; ++i)
753 context.draw_surface(tux_life,
754 Vector(screen->w - tux_life->w*4 +(tux_life->w*i), 20),
758 context.draw_text(white_text, _("LIVES"),
759 Vector(screen->w - white_text->get_text_width(_("LIVES")) - white_text->get_text_width(" 99"), 20),
764 sprintf(str, "%2.1f", fps_fps);
765 context.draw_text(white_text, "FPS",
766 Vector(screen->w - white_text->get_text_width("FPS "), 40),
768 context.draw_text(gold_text, str,
769 Vector(screen->w-4*16, 40), LAYER_FOREGROUND1);
774 GameSession::drawresultscreen(void)
778 DrawingContext context;
779 currentsector->background->draw(context);
781 context.draw_text_center(blue_text, _("Result:"), Vector(0, 200),
784 sprintf(str, _("SCORE: %d"), player_status.score);
785 context.draw_text_center(gold_text, str, Vector(0, 224), LAYER_FOREGROUND1);
787 sprintf(str, _("COINS: %d"), player_status.distros);
788 context.draw_text_center(gold_text, str, Vector(0, 256), LAYER_FOREGROUND1);
790 context.do_drawing();
793 wait_for_event(event,2000,5000,true);
796 std::string slotinfo(int slot)
801 sprintf(slotfile,"%s/slot%d.stsg",st_save_dir,slot);
803 lisp_object_t* savegame = lisp_read_from_file(slotfile);
806 LispReader reader(lisp_cdr(savegame));
807 reader.read_string("title", title);
811 if (access(slotfile, F_OK) == 0)
814 snprintf(tmp,1024,"Slot %d - %s",slot, title.c_str());
816 snprintf(tmp, 1024,_("Slot %d - Savegame"),slot);
819 sprintf(tmp,_("Slot %d - Free"),slot);