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"
58 #include "music_manager.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 music_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))
325 st_pause_ticks_stop();
330 st_pause_ticks_start();
337 tux.size = !tux.size;
340 tux.base.height = 64;
343 tux.base.height = 32;
348 player_status.distros += 50;
352 tux.got_power = tux.FIRE_POWER;
356 tux.got_power = tux.ICE_POWER;
360 tux.invincible_timer.start(TUX_INVINCIBLE_TIME);
364 --player_status.lives;
368 player_status.score += 1000;
381 case SDL_JOYAXISMOTION:
382 if (event.jaxis.axis == joystick_keymap.x_axis)
384 if (event.jaxis.value < -joystick_keymap.dead_zone)
386 tux.input.left = DOWN;
387 tux.input.right = UP;
389 else if (event.jaxis.value > joystick_keymap.dead_zone)
392 tux.input.right = DOWN;
396 tux.input.left = DOWN;
397 tux.input.right = DOWN;
400 else if (event.jaxis.axis == joystick_keymap.y_axis)
402 if (event.jaxis.value > joystick_keymap.dead_zone)
403 tux.input.down = DOWN;
404 else if (event.jaxis.value < -joystick_keymap.dead_zone)
411 case SDL_JOYBUTTONDOWN:
412 if (event.jbutton.button == joystick_keymap.a_button)
414 else if (event.jbutton.button == joystick_keymap.b_button)
415 tux.input.fire = DOWN;
416 else if (event.jbutton.button == joystick_keymap.start_button)
419 case SDL_JOYBUTTONUP:
420 if (event.jbutton.button == joystick_keymap.a_button)
422 else if (event.jbutton.button == joystick_keymap.b_button)
435 GameSession::check_end_conditions()
437 Player* tux = currentsector->player;
440 int endpos = (currentsector->solids->get_width() - 5) * 32;
441 Tile* endtile = collision_goal(tux->base);
443 // fallback in case the other endpositions don't trigger
444 if (!end_sequence && tux->base.x >= endpos)
446 end_sequence = ENDSEQUENCE_WAITING;
448 music_manager->play_music(level_end_song, 0);
449 endsequence_timer.start(7000);
450 tux->invincible_timer.start(7000); //FIXME: Implement a winning timer for the end sequence (with special winning animation etc.)
452 else if(end_sequence && !endsequence_timer.check())
454 exit_status = ES_LEVEL_FINISHED;
457 else if(end_sequence == ENDSEQUENCE_RUNNING && endtile && endtile->data >= 1)
459 end_sequence = ENDSEQUENCE_WAITING;
461 else if(!end_sequence && endtile && endtile->data == 0)
463 end_sequence = ENDSEQUENCE_RUNNING;
465 music_manager->play_music(level_end_song, 0);
466 endsequence_timer.start(7000); // 5 seconds until we finish the map
467 tux->invincible_timer.start(7000); //FIXME: Implement a winning timer for the end sequence (with special winning animation etc.)
469 else if (!end_sequence && tux->is_dead())
471 player_status.bonus = PlayerStatus::NO_BONUS;
473 if (player_status.lives < 0)
475 exit_status = ES_GAME_OVER;
478 { // Still has lives, so reset Tux to the levelstart
487 GameSession::action(double frame_ratio)
489 if (exit_status == ES_NONE && !currentsector->player->growing_timer.check())
491 // Update Tux and the World
492 currentsector->action(frame_ratio);
499 currentsector->draw(*context);
500 drawstatus(*context);
504 int x = screen->h / 20;
505 for(int i = 0; i < x; ++i)
507 context->draw_filled_rect(
508 Vector(i % 2 ? (pause_menu_frame * i)%screen->w :
509 -((pause_menu_frame * i)%screen->w)
510 ,(i*20+pause_menu_frame)%screen->h),
511 Vector(screen->w,10),
512 Color(20,20,20, rand() % 20 + 1), LAYER_FOREGROUND1+1);
514 context->draw_filled_rect(
515 Vector(0,0), Vector(screen->w, screen->h),
516 Color(rand() % 50, rand() % 50, rand() % 50, 128), LAYER_FOREGROUND1);
517 context->draw_text_center(blue_text, "PAUSE - Press 'P' To Play",
518 Vector(0, 230), LAYER_FOREGROUND1+2);
523 Menu::current()->draw(*context);
524 mouse_cursor->draw(*context);
527 context->do_drawing();
531 GameSession::process_menu()
533 Menu* menu = Menu::current();
538 if(menu == game_menu)
540 switch (game_menu->check())
543 st_pause_ticks_stop();
545 case MNID_ABORTLEVEL:
546 st_pause_ticks_stop();
547 exit_status = ES_LEVEL_ABORT;
551 else if(menu == options_menu)
553 process_options_menu();
555 else if(menu == load_game_menu )
557 process_load_game_menu();
562 GameSession::ExitStatus
565 Menu::set_current(0);
570 update_time = last_update_time = st_get_ticks();
572 // Eat unneeded events
574 while (SDL_PollEvent(&event)) {}
578 while (exit_status == ES_NONE)
580 /* Calculate the movement-factor */
581 double frame_ratio = ((double)(update_time-last_update_time))/((double)FRAME_RATE);
583 if(!frame_timer.check())
585 frame_timer.start(25);
586 ++global_frame_counter;
590 currentsector->player->input.old_fire
591 = currentsector->player->input.fire;
596 // Update the world state and all objects in the world
597 // Do that with a constante time-delta so that the game will run
598 // determistic and not different on different machines
599 if(!game_pause && !Menu::current())
602 check_end_conditions();
603 if (end_sequence == ENDSEQUENCE_RUNNING)
604 action(frame_ratio/2);
605 else if(end_sequence == NO_ENDSEQUENCE)
616 /* Time stops in pause mode */
617 if(game_pause || Menu::current())
622 /* Set the time of the last update and the time of the current update */
623 last_update_time = update_time;
624 update_time = st_get_ticks();
626 /* Pause till next frame, if the machine running the game is too fast: */
627 /* FIXME: Works great for in OpenGl mode, where the CPU doesn't have to do that much. But
628 the results in SDL mode aren't perfect (thought the 100 FPS are reached), even on an AMD2500+. */
629 if(last_update_time >= update_time - 12)
632 update_time = st_get_ticks();
636 if (!time_left.check() && currentsector->player->dying == DYING_NOT
638 currentsector->player->kill(Player::KILL);
641 if(currentsector->player->invincible_timer.check() && !end_sequence)
643 currentsector->play_music(HERRING_MUSIC);
645 /* are we low on time ? */
646 else if (time_left.get_left() < TIME_WARNING && !end_sequence)
648 currentsector->play_music(HURRYUP_MUSIC);
650 /* or just normal music? */
651 else if(currentsector->get_music_type() != LEVEL_MUSIC && !end_sequence)
653 currentsector->play_music(LEVEL_MUSIC);
656 /* Calculate frames per second */
660 fps_fps = (1000.0 / (float)fps_timer.get_gone()) * (float)fps_cnt;
662 if(!fps_timer.check())
664 fps_timer.start(1000);
673 /* Bounce a brick: */
674 void bumpbrick(float x, float y)
676 Sector::current()->add_bouncy_brick(Vector(((int)(x + 1) / 32) * 32,
677 (int)(y / 32) * 32));
679 play_sound(sounds[SND_BRICK], SOUND_CENTER_SPEAKER);
684 GameSession::drawstatus(DrawingContext& context)
688 snprintf(str, 60, "%d", player_status.score);
689 context.draw_text(white_text, "SCORE", Vector(0, 0), LAYER_FOREGROUND1);
690 context.draw_text(gold_text, str, Vector(96, 0), LAYER_FOREGROUND1);
692 if(st_gl_mode == ST_GL_TEST)
694 context.draw_text(white_text, "Press ESC To Return", Vector(0,20),
698 if(!time_left.check()) {
699 context.draw_text_center(white_text, "TIME's UP", Vector(0, 0),
701 } else if (time_left.get_left() > TIME_WARNING || (global_frame_counter % 10) < 5) {
702 sprintf(str, "%d", time_left.get_left() / 1000 );
703 context.draw_text_center(white_text, "TIME",
704 Vector(0, 0), LAYER_FOREGROUND1);
705 context.draw_text_center(gold_text, str,
706 Vector(4*16, 0), LAYER_FOREGROUND1);
709 sprintf(str, "%d", player_status.distros);
710 context.draw_text(white_text, "COINS",
711 Vector(screen->w - white_text->w*9, 0), LAYER_FOREGROUND1);
712 context.draw_text(gold_text, str,
713 Vector(screen->w - gold_text->w*2, 0), LAYER_FOREGROUND1);
715 context.draw_text(white_text, "LIVES",
716 Vector(screen->w - white_text->w*9, 20), LAYER_FOREGROUND1);
717 if (player_status.lives >= 5)
719 sprintf(str, "%dx", player_status.lives);
720 float x = screen->w - gold_text->get_text_width(str) - tux_life->w;
721 context.draw_text(gold_text, str, Vector(x, 20), LAYER_FOREGROUND1);
722 context.draw_surface(tux_life, Vector(screen->w - 16, 20),
727 for(int i= 0; i < player_status.lives; ++i)
728 context.draw_surface(tux_life,
729 Vector(screen->w - tux_life->w*4 +(tux_life->w*i), 20),
735 sprintf(str, "%2.1f", fps_fps);
736 context.draw_text(white_text, "FPS",
737 Vector(screen->w - white_text->w*9, 40), LAYER_FOREGROUND1);
738 context.draw_text(gold_text, str,
739 Vector(screen->w-4*16, 40), LAYER_FOREGROUND1);
744 GameSession::drawresultscreen(void)
748 DrawingContext context;
749 currentsector->background->draw(context);
751 context.draw_text_center(blue_text, "Result:", Vector(0, 200),
754 sprintf(str, "SCORE: %d", player_status.score);
755 context.draw_text_center(gold_text, str, Vector(0, 224), LAYER_FOREGROUND1);
757 sprintf(str, "COINS: %d", player_status.distros);
758 context.draw_text_center(gold_text, str, Vector(0, 256), LAYER_FOREGROUND1);
760 context.do_drawing();
763 wait_for_event(event,2000,5000,true);
766 std::string slotinfo(int slot)
771 sprintf(slotfile,"%s/slot%d.stsg",st_save_dir,slot);
773 lisp_object_t* savegame = lisp_read_from_file(slotfile);
776 LispReader reader(lisp_cdr(savegame));
777 reader.read_string("title", title);
781 if (access(slotfile, F_OK) == 0)
784 snprintf(tmp,1024,"Slot %d - %s",slot, title.c_str());
786 snprintf(tmp, 1024,"Slot %d - Savegame",slot);
789 sprintf(tmp,"Slot %d - Free",slot);