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);
168 if(level->is_level_flipped())
169 context.draw_text_center(white_text,
170 _("Level Vertically Flipped!"),
171 Vector(0, 310), LAYER_FOREGROUND1);
173 context.do_drawing();
176 wait_for_event(event,1000,3000,true);
181 GameSession::start_timers()
183 time_left.start(level->time_left*1000);
184 st_pause_ticks_init();
185 update_time = st_get_ticks();
189 GameSession::on_escape_press()
191 if(currentsector->player->dying || end_sequence != NO_ENDSEQUENCE)
192 return; // don't let the player open the menu, when he is dying
197 if(st_gl_mode == ST_GL_TEST)
199 exit_status = ES_LEVEL_ABORT;
201 else if (!Menu::current())
203 /* Tell Tux that the keys are all down, otherwise
204 it could have nasty bugs, like going allways to the right
205 or whatever that key does */
206 Player& tux = *(currentsector->player);
207 tux.key_event((SDLKey)keymap.jump, UP);
208 tux.key_event((SDLKey)keymap.duck, UP);
209 tux.key_event((SDLKey)keymap.left, UP);
210 tux.key_event((SDLKey)keymap.right, UP);
211 tux.key_event((SDLKey)keymap.fire, UP);
213 Menu::set_current(game_menu);
214 st_pause_ticks_start();
219 GameSession::process_events()
221 if (end_sequence != NO_ENDSEQUENCE)
223 Player& tux = *currentsector->player;
227 tux.input.right = DOWN;
230 if (int(last_x_pos) == int(tux.base.x))
235 last_x_pos = tux.base.x;
238 while (SDL_PollEvent(&event))
240 /* Check for menu-events, if the menu is shown */
243 Menu::current()->event(event);
245 st_pause_ticks_stop();
250 case SDL_QUIT: /* Quit event - quit: */
251 st_abort("Received window close", "");
254 case SDL_KEYDOWN: /* A keypress! */
256 SDLKey key = event.key.keysym.sym;
260 case SDLK_ESCAPE: /* Escape: Open/Close the menu: */
268 case SDL_JOYBUTTONDOWN:
269 if (event.jbutton.button == joystick_keymap.start_button)
277 if(!Menu::current() && !game_pause)
278 st_pause_ticks_stop();
281 while (SDL_PollEvent(&event))
283 /* Check for menu-events, if the menu is shown */
286 Menu::current()->event(event);
288 st_pause_ticks_stop();
292 Player& tux = *currentsector->player;
296 case SDL_QUIT: /* Quit event - quit: */
297 st_abort("Received window close", "");
300 case SDL_KEYDOWN: /* A keypress! */
302 SDLKey key = event.key.keysym.sym;
304 if(tux.key_event(key,DOWN))
309 case SDLK_ESCAPE: /* Escape: Open/Close the menu: */
317 case SDL_KEYUP: /* A keyrelease! */
319 SDLKey key = event.key.keysym.sym;
321 if(tux.key_event(key, UP))
330 snprintf(buf, sizeof(buf), "P: %4.1f,%4.1f",
331 tux.base.x, tux.base.y);
332 context->draw_text(white_text, buf,
333 Vector(0, screen->h - white_text->get_height()),
335 context->do_drawing();
345 st_pause_ticks_stop();
350 st_pause_ticks_start();
362 player_status.distros += 50;
366 tux.got_power = tux.FIRE_POWER;
370 tux.got_power = tux.ICE_POWER;
374 tux.invincible_timer.start(TUX_INVINCIBLE_TIME);
378 --player_status.lives;
382 player_status.score += 1000;
395 case SDL_JOYAXISMOTION:
396 if (event.jaxis.axis == joystick_keymap.x_axis)
398 if (event.jaxis.value < -joystick_keymap.dead_zone)
400 tux.input.left = DOWN;
401 tux.input.right = UP;
403 else if (event.jaxis.value > joystick_keymap.dead_zone)
406 tux.input.right = DOWN;
410 tux.input.left = DOWN;
411 tux.input.right = DOWN;
414 else if (event.jaxis.axis == joystick_keymap.y_axis)
416 if (event.jaxis.value > joystick_keymap.dead_zone)
417 tux.input.down = DOWN;
418 else if (event.jaxis.value < -joystick_keymap.dead_zone)
425 case SDL_JOYBUTTONDOWN:
426 if (event.jbutton.button == joystick_keymap.a_button)
428 else if (event.jbutton.button == joystick_keymap.b_button)
429 tux.input.fire = DOWN;
430 else if (event.jbutton.button == joystick_keymap.start_button)
433 case SDL_JOYBUTTONUP:
434 if (event.jbutton.button == joystick_keymap.a_button)
436 else if (event.jbutton.button == joystick_keymap.b_button)
449 GameSession::check_end_conditions()
451 Player* tux = currentsector->player;
454 Tile* endtile = collision_goal(tux->base);
456 if(end_sequence && !endsequence_timer.check())
458 exit_status = ES_LEVEL_FINISHED;
461 else if(end_sequence == ENDSEQUENCE_RUNNING && endtile && endtile->data >= 1)
463 end_sequence = ENDSEQUENCE_WAITING;
465 else if(!end_sequence && endtile && endtile->data == 0)
467 end_sequence = ENDSEQUENCE_RUNNING;
469 sound_manager->play_music(level_end_song, 0);
470 endsequence_timer.start(7000); // 5 seconds until we finish the map
471 tux->invincible_timer.start(7000); //FIXME: Implement a winning timer for the end sequence (with special winning animation etc.)
473 else if (!end_sequence && tux->is_dead())
475 player_status.bonus = PlayerStatus::NO_BONUS;
477 if (player_status.lives < 0)
479 exit_status = ES_GAME_OVER;
482 { // Still has lives, so reset Tux to the levelstart
491 GameSession::action(double frame_ratio)
493 if (exit_status == ES_NONE && !currentsector->player->growing_timer.check())
495 // Update Tux and the World
496 currentsector->action(frame_ratio);
499 // respawning in new sector?
500 if(newsector != "" && newspawnpoint != "") {
501 Sector* sector = level->get_sector(newsector);
502 currentsector = sector;
503 currentsector->activate(newspawnpoint);
504 currentsector->play_music(LEVEL_MUSIC);
505 newsector = newspawnpoint = "";
512 currentsector->draw(*context);
513 drawstatus(*context);
517 int x = screen->h / 20;
518 for(int i = 0; i < x; ++i)
520 context->draw_filled_rect(
521 Vector(i % 2 ? (pause_menu_frame * i)%screen->w :
522 -((pause_menu_frame * i)%screen->w)
523 ,(i*20+pause_menu_frame)%screen->h),
524 Vector(screen->w,10),
525 Color(20,20,20, rand() % 20 + 1), LAYER_FOREGROUND1+1);
527 context->draw_filled_rect(
528 Vector(0,0), Vector(screen->w, screen->h),
529 Color(rand() % 50, rand() % 50, rand() % 50, 128), LAYER_FOREGROUND1);
530 context->draw_text_center(blue_text, _("PAUSE - Press 'P' To Play"),
531 Vector(0, 230), LAYER_FOREGROUND1+2);
535 sprintf(str1, _("Playing: "));
536 sprintf(str2, level->name.c_str());
538 context->draw_text(blue_text, str1,
539 Vector((screen->w - (blue_text->get_text_width(str1) + white_text->get_text_width(str2)))/2, 340),
540 LAYER_FOREGROUND1+2);
541 context->draw_text(white_text, str2,
542 Vector(((screen->w - (blue_text->get_text_width(str1) + white_text->get_text_width(str2)))/2)+blue_text->get_text_width(str1), 340),
543 LAYER_FOREGROUND1+2);
548 Menu::current()->draw(*context);
549 mouse_cursor->draw(*context);
552 context->do_drawing();
556 GameSession::process_menu()
558 Menu* menu = Menu::current();
563 if(menu == game_menu)
565 switch (game_menu->check())
568 st_pause_ticks_stop();
570 case MNID_ABORTLEVEL:
571 st_pause_ticks_stop();
572 exit_status = ES_LEVEL_ABORT;
576 else if(menu == options_menu)
578 process_options_menu();
580 else if(menu == load_game_menu )
582 process_load_game_menu();
587 GameSession::ExitStatus
590 Menu::set_current(0);
595 update_time = last_update_time = st_get_ticks();
597 // Eat unneeded events
599 while (SDL_PollEvent(&event)) {}
603 while (exit_status == ES_NONE)
605 /* Calculate the movement-factor */
606 double frame_ratio = ((double)(update_time-last_update_time))/((double)FRAME_RATE);
608 if(!frame_timer.check())
610 frame_timer.start(25);
611 ++global_frame_counter;
615 currentsector->player->input.old_fire
616 = currentsector->player->input.fire;
621 // Update the world state and all objects in the world
622 // Do that with a constante time-delta so that the game will run
623 // determistic and not different on different machines
624 if(!game_pause && !Menu::current())
627 check_end_conditions();
628 if (end_sequence == ENDSEQUENCE_RUNNING)
629 action(frame_ratio/2);
630 else if(end_sequence == NO_ENDSEQUENCE)
641 /* Time stops in pause mode */
642 if(game_pause || Menu::current())
647 /* Set the time of the last update and the time of the current update */
648 last_update_time = update_time;
649 update_time = st_get_ticks();
651 /* Pause till next frame, if the machine running the game is too fast: */
652 /* FIXME: Works great for in OpenGl mode, where the CPU doesn't have to do that much. But
653 the results in SDL mode aren't perfect (thought the 100 FPS are reached), even on an AMD2500+. */
654 if(last_update_time >= update_time - 12)
657 update_time = st_get_ticks();
661 if (!time_left.check() && currentsector->player->dying == DYING_NOT
663 currentsector->player->kill(Player::KILL);
666 if(currentsector->player->invincible_timer.check() && !end_sequence)
668 currentsector->play_music(HERRING_MUSIC);
670 /* are we low on time ? */
671 else if (time_left.get_left() < TIME_WARNING && !end_sequence)
673 currentsector->play_music(HURRYUP_MUSIC);
675 /* or just normal music? */
676 else if(currentsector->get_music_type() != LEVEL_MUSIC && !end_sequence)
678 currentsector->play_music(LEVEL_MUSIC);
681 /* Calculate frames per second */
685 fps_fps = (1000.0 / (float)fps_timer.get_gone()) * (float)fps_cnt;
687 if(!fps_timer.check())
689 fps_timer.start(1000);
699 GameSession::respawn(const std::string& sector, const std::string& spawnpoint)
702 newspawnpoint = spawnpoint;
705 /* Bounce a brick: */
706 void bumpbrick(float x, float y)
708 Sector::current()->add_bouncy_brick(Vector(((int)(x + 1) / 32) * 32,
709 (int)(y / 32) * 32));
711 sound_manager->play_sound(sounds[SND_BRICK], Vector(x, y));
716 GameSession::drawstatus(DrawingContext& context)
720 snprintf(str, 60, " %d", player_status.score);
721 context.draw_text(white_text, _("SCORE"), Vector(0, 0), LAYER_FOREGROUND1);
722 context.draw_text(gold_text, str, Vector(96, 0), LAYER_FOREGROUND1);
724 if(st_gl_mode == ST_GL_TEST)
726 context.draw_text(white_text, _("Press ESC To Return"), Vector(0,20),
730 if(!time_left.check()) {
731 context.draw_text_center(white_text, _("TIME's UP"), Vector(0, 0),
733 } else if (time_left.get_left() > TIME_WARNING || (global_frame_counter % 10) < 5) {
734 sprintf(str, " %d", time_left.get_left() / 1000 );
735 context.draw_text_center(white_text, _("TIME"),
736 Vector(0, 0), LAYER_FOREGROUND1);
737 context.draw_text_center(gold_text, str,
738 Vector(4*16, 0), LAYER_FOREGROUND1);
741 sprintf(str, " %d", player_status.distros);
742 context.draw_text(white_text, _("COINS"),
743 Vector(screen->w - white_text->get_text_width(_("COINS"))-white_text->get_text_width(" 99"), 0),
745 context.draw_text(gold_text, str,
746 Vector(screen->w - gold_text->get_text_width(" 99"), 0),LAYER_FOREGROUND1);
748 if (player_status.lives >= 5)
750 sprintf(str, "%dx", player_status.lives);
751 float x = screen->w - gold_text->get_text_width(str) - tux_life->w;
752 context.draw_text(gold_text, str, Vector(x, 20), LAYER_FOREGROUND1);
753 context.draw_surface(tux_life, Vector(screen->w - 16, 20),
758 for(int i= 0; i < player_status.lives; ++i)
759 context.draw_surface(tux_life,
760 Vector(screen->w - tux_life->w*4 +(tux_life->w*i), 20),
764 context.draw_text(white_text, _("LIVES"),
765 Vector(screen->w - white_text->get_text_width(_("LIVES")) - white_text->get_text_width(" 99"), 20),
770 sprintf(str, "%2.1f", fps_fps);
771 context.draw_text(white_text, "FPS",
772 Vector(screen->w - white_text->get_text_width("FPS "), 40),
774 context.draw_text(gold_text, str,
775 Vector(screen->w-4*16, 40), LAYER_FOREGROUND1);
780 GameSession::drawresultscreen(void)
784 DrawingContext context;
785 currentsector->background->draw(context);
787 context.draw_text_center(blue_text, _("Result:"), Vector(0, 200),
790 sprintf(str, _("SCORE: %d"), player_status.score);
791 context.draw_text_center(gold_text, str, Vector(0, 224), LAYER_FOREGROUND1);
793 sprintf(str, _("COINS: %d"), player_status.distros);
794 context.draw_text_center(gold_text, str, Vector(0, 256), LAYER_FOREGROUND1);
796 context.do_drawing();
799 wait_for_event(event,2000,5000,true);
802 std::string slotinfo(int slot)
807 sprintf(slotfile,"%s/slot%d.stsg",st_save_dir,slot);
809 lisp_object_t* savegame = lisp_read_from_file(slotfile);
812 LispReader reader(lisp_cdr(savegame));
813 reader.read_string("title", title);
817 if (access(slotfile, F_OK) == 0)
820 snprintf(tmp,1024,"Slot %d - %s",slot, title.c_str());
822 snprintf(tmp, 1024,_("Slot %d - Savegame"),slot);
825 sprintf(tmp,_("Slot %d - Free"),slot);