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"
59 GameSession* GameSession::current_ = 0;
61 GameSession::GameSession(const std::string& levelname_, int mode)
62 : level(0), currentsector(0), st_gl_mode(mode),
63 end_sequence(NO_ENDSEQUENCE), levelname(levelname_)
67 global_frame_counter = 0;
72 frame_timer.init(true);
74 context = new DrawingContext();
80 GameSession::restart_level()
83 exit_status = ES_NONE;
84 end_sequence = NO_ENDSEQUENCE;
87 frame_timer.init(true);
92 { // Tux has lost a life, so we try to respawn him at the nearest reset point
93 old_x_pos = world->get_tux()->base.x;
101 level->load(levelname);
102 currentsector = level->get_sector("main");
104 st_abort("Level has no main sector.", "");
105 currentsector->activate("main");
108 // Set Tux to the nearest reset point
111 ResetPoint best_reset_point = { -1, -1 };
112 for(std::vector<ResetPoint>::iterator i = get_level()->reset_points.begin();
113 i != get_level()->reset_points.end(); ++i)
115 if (i->x < old_x_pos && best_reset_point.x < i->x)
116 best_reset_point = *i;
119 if (best_reset_point.x != -1)
121 world->get_tux()->base.x = best_reset_point.x;
122 world->get_tux()->base.y = best_reset_point.y;
127 if (st_gl_mode != ST_GL_DEMO_GAME)
129 if(st_gl_mode == ST_GL_PLAY || st_gl_mode == ST_GL_LOAD_LEVEL_FILE)
133 time_left.init(true);
135 currentsector->play_music(LEVEL_MUSIC);
138 GameSession::~GameSession()
145 GameSession::levelintro(void)
147 sound_manager->halt_music();
151 DrawingContext context;
152 currentsector->background->draw(context);
154 context.draw_text_center(gold_text, level->get_name(), Vector(0, 220),
157 sprintf(str, "TUX x %d", player_status.lives);
158 context.draw_text_center(white_text, str, Vector(0, 240),
161 context.draw_text_center(white_small_text,
162 std::string("by ") + level->get_author(),
163 Vector(0, 400), LAYER_FOREGROUND1);
165 context.do_drawing();
168 wait_for_event(event,1000,3000,true);
173 GameSession::start_timers()
175 time_left.start(level->time_left*1000);
176 st_pause_ticks_init();
177 update_time = st_get_ticks();
181 GameSession::on_escape_press()
183 if(currentsector->player->dying || end_sequence != NO_ENDSEQUENCE)
184 return; // don't let the player open the menu, when he is dying
189 if(st_gl_mode == ST_GL_TEST)
191 exit_status = ES_LEVEL_ABORT;
193 else if (!Menu::current())
195 /* Tell Tux that the keys are all down, otherwise
196 it could have nasty bugs, like going allways to the right
197 or whatever that key does */
198 Player& tux = *(currentsector->player);
199 tux.key_event((SDLKey)keymap.jump, UP);
200 tux.key_event((SDLKey)keymap.duck, UP);
201 tux.key_event((SDLKey)keymap.left, UP);
202 tux.key_event((SDLKey)keymap.right, UP);
203 tux.key_event((SDLKey)keymap.fire, UP);
205 Menu::set_current(game_menu);
206 st_pause_ticks_start();
211 GameSession::process_events()
213 if (end_sequence != NO_ENDSEQUENCE)
215 Player& tux = *currentsector->player;
219 tux.input.right = DOWN;
222 if (int(last_x_pos) == int(tux.base.x))
227 last_x_pos = tux.base.x;
230 while (SDL_PollEvent(&event))
232 /* Check for menu-events, if the menu is shown */
235 Menu::current()->event(event);
237 st_pause_ticks_stop();
242 case SDL_QUIT: /* Quit event - quit: */
243 st_abort("Received window close", "");
246 case SDL_KEYDOWN: /* A keypress! */
248 SDLKey key = event.key.keysym.sym;
252 case SDLK_ESCAPE: /* Escape: Open/Close the menu: */
260 case SDL_JOYBUTTONDOWN:
261 if (event.jbutton.button == joystick_keymap.start_button)
269 if(!Menu::current() && !game_pause)
270 st_pause_ticks_stop();
273 while (SDL_PollEvent(&event))
275 /* Check for menu-events, if the menu is shown */
278 Menu::current()->event(event);
280 st_pause_ticks_stop();
284 Player& tux = *currentsector->player;
288 case SDL_QUIT: /* Quit event - quit: */
289 st_abort("Received window close", "");
292 case SDL_KEYDOWN: /* A keypress! */
294 SDLKey key = event.key.keysym.sym;
296 if(tux.key_event(key,DOWN))
301 case SDLK_ESCAPE: /* Escape: Open/Close the menu: */
309 case SDL_KEYUP: /* A keyrelease! */
311 SDLKey key = event.key.keysym.sym;
313 if(tux.key_event(key, UP))
322 snprintf(buf, sizeof(buf), "P: %4.1f,%4.1f",
323 tux.base.x, tux.base.y);
324 context->draw_text(white_text, buf,
325 Vector(0, screen->h - white_text->get_height()),
327 context->do_drawing();
337 st_pause_ticks_stop();
342 st_pause_ticks_start();
354 player_status.distros += 50;
358 tux.got_power = tux.FIRE_POWER;
362 tux.got_power = tux.ICE_POWER;
366 tux.invincible_timer.start(TUX_INVINCIBLE_TIME);
370 --player_status.lives;
374 player_status.score += 1000;
387 case SDL_JOYAXISMOTION:
388 if (event.jaxis.axis == joystick_keymap.x_axis)
390 if (event.jaxis.value < -joystick_keymap.dead_zone)
392 tux.input.left = DOWN;
393 tux.input.right = UP;
395 else if (event.jaxis.value > joystick_keymap.dead_zone)
398 tux.input.right = DOWN;
402 tux.input.left = DOWN;
403 tux.input.right = DOWN;
406 else if (event.jaxis.axis == joystick_keymap.y_axis)
408 if (event.jaxis.value > joystick_keymap.dead_zone)
409 tux.input.down = DOWN;
410 else if (event.jaxis.value < -joystick_keymap.dead_zone)
417 case SDL_JOYBUTTONDOWN:
418 if (event.jbutton.button == joystick_keymap.a_button)
420 else if (event.jbutton.button == joystick_keymap.b_button)
421 tux.input.fire = DOWN;
422 else if (event.jbutton.button == joystick_keymap.start_button)
425 case SDL_JOYBUTTONUP:
426 if (event.jbutton.button == joystick_keymap.a_button)
428 else if (event.jbutton.button == joystick_keymap.b_button)
441 GameSession::check_end_conditions()
443 Player* tux = currentsector->player;
446 Tile* endtile = collision_goal(tux->base);
448 if(end_sequence && !endsequence_timer.check())
450 exit_status = ES_LEVEL_FINISHED;
453 else if(end_sequence == ENDSEQUENCE_RUNNING && endtile && endtile->data >= 1)
455 end_sequence = ENDSEQUENCE_WAITING;
457 else if(!end_sequence && endtile && endtile->data == 0)
459 end_sequence = ENDSEQUENCE_RUNNING;
461 sound_manager->play_music(level_end_song, 0);
462 endsequence_timer.start(7000); // 5 seconds until we finish the map
463 tux->invincible_timer.start(7000); //FIXME: Implement a winning timer for the end sequence (with special winning animation etc.)
465 else if (!end_sequence && tux->is_dead())
467 player_status.bonus = PlayerStatus::NO_BONUS;
469 if (player_status.lives < 0)
471 exit_status = ES_GAME_OVER;
474 { // Still has lives, so reset Tux to the levelstart
483 GameSession::action(double frame_ratio)
485 if (exit_status == ES_NONE && !currentsector->player->growing_timer.check())
487 // Update Tux and the World
488 currentsector->action(frame_ratio);
491 // respawning in new sector?
492 if(newsector != "" && newspawnpoint != "") {
493 Sector* sector = level->get_sector(newsector);
494 currentsector = sector;
495 currentsector->activate(newspawnpoint);
496 currentsector->play_music(LEVEL_MUSIC);
497 newsector = newspawnpoint = "";
504 currentsector->draw(*context);
505 drawstatus(*context);
509 int x = screen->h / 20;
510 for(int i = 0; i < x; ++i)
512 context->draw_filled_rect(
513 Vector(i % 2 ? (pause_menu_frame * i)%screen->w :
514 -((pause_menu_frame * i)%screen->w)
515 ,(i*20+pause_menu_frame)%screen->h),
516 Vector(screen->w,10),
517 Color(20,20,20, rand() % 20 + 1), LAYER_FOREGROUND1+1);
519 context->draw_filled_rect(
520 Vector(0,0), Vector(screen->w, screen->h),
521 Color(rand() % 50, rand() % 50, rand() % 50, 128), LAYER_FOREGROUND1);
522 context->draw_text_center(blue_text, "PAUSE - Press 'P' To Play",
523 Vector(0, 230), LAYER_FOREGROUND1+2);
528 Menu::current()->draw(*context);
529 mouse_cursor->draw(*context);
532 context->do_drawing();
536 GameSession::process_menu()
538 Menu* menu = Menu::current();
543 if(menu == game_menu)
545 switch (game_menu->check())
548 st_pause_ticks_stop();
550 case MNID_ABORTLEVEL:
551 st_pause_ticks_stop();
552 exit_status = ES_LEVEL_ABORT;
556 else if(menu == options_menu)
558 process_options_menu();
560 else if(menu == load_game_menu )
562 process_load_game_menu();
567 GameSession::ExitStatus
570 Menu::set_current(0);
575 update_time = last_update_time = st_get_ticks();
577 // Eat unneeded events
579 while (SDL_PollEvent(&event)) {}
583 while (exit_status == ES_NONE)
585 /* Calculate the movement-factor */
586 double frame_ratio = ((double)(update_time-last_update_time))/((double)FRAME_RATE);
588 if(!frame_timer.check())
590 frame_timer.start(25);
591 ++global_frame_counter;
595 currentsector->player->input.old_fire
596 = currentsector->player->input.fire;
601 // Update the world state and all objects in the world
602 // Do that with a constante time-delta so that the game will run
603 // determistic and not different on different machines
604 if(!game_pause && !Menu::current())
607 check_end_conditions();
608 if (end_sequence == ENDSEQUENCE_RUNNING)
609 action(frame_ratio/2);
610 else if(end_sequence == NO_ENDSEQUENCE)
621 /* Time stops in pause mode */
622 if(game_pause || Menu::current())
627 /* Set the time of the last update and the time of the current update */
628 last_update_time = update_time;
629 update_time = st_get_ticks();
631 /* Pause till next frame, if the machine running the game is too fast: */
632 /* FIXME: Works great for in OpenGl mode, where the CPU doesn't have to do that much. But
633 the results in SDL mode aren't perfect (thought the 100 FPS are reached), even on an AMD2500+. */
634 if(last_update_time >= update_time - 12)
637 update_time = st_get_ticks();
641 if (!time_left.check() && currentsector->player->dying == DYING_NOT
643 currentsector->player->kill(Player::KILL);
646 if(currentsector->player->invincible_timer.check() && !end_sequence)
648 currentsector->play_music(HERRING_MUSIC);
650 /* are we low on time ? */
651 else if (time_left.get_left() < TIME_WARNING && !end_sequence)
653 currentsector->play_music(HURRYUP_MUSIC);
655 /* or just normal music? */
656 else if(currentsector->get_music_type() != LEVEL_MUSIC && !end_sequence)
658 currentsector->play_music(LEVEL_MUSIC);
661 /* Calculate frames per second */
665 fps_fps = (1000.0 / (float)fps_timer.get_gone()) * (float)fps_cnt;
667 if(!fps_timer.check())
669 fps_timer.start(1000);
679 GameSession::respawn(const std::string& sector, const std::string& spawnpoint)
682 newspawnpoint = spawnpoint;
685 /* Bounce a brick: */
686 void bumpbrick(float x, float y)
688 Sector::current()->add_bouncy_brick(Vector(((int)(x + 1) / 32) * 32,
689 (int)(y / 32) * 32));
691 sound_manager->play_sound(sounds[SND_BRICK], Vector(x, y));
696 GameSession::drawstatus(DrawingContext& context)
700 snprintf(str, 60, "%d", player_status.score);
701 context.draw_text(white_text, "SCORE", Vector(0, 0), LAYER_FOREGROUND1);
702 context.draw_text(gold_text, str, Vector(96, 0), LAYER_FOREGROUND1);
704 if(st_gl_mode == ST_GL_TEST)
706 context.draw_text(white_text, "Press ESC To Return", Vector(0,20),
710 if(!time_left.check()) {
711 context.draw_text_center(white_text, "TIME's UP", Vector(0, 0),
713 } else if (time_left.get_left() > TIME_WARNING || (global_frame_counter % 10) < 5) {
714 sprintf(str, "%d", time_left.get_left() / 1000 );
715 context.draw_text_center(white_text, "TIME",
716 Vector(0, 0), LAYER_FOREGROUND1);
717 context.draw_text_center(gold_text, str,
718 Vector(4*16, 0), LAYER_FOREGROUND1);
721 sprintf(str, "%d", player_status.distros);
722 context.draw_text(white_text, "COINS",
723 Vector(screen->w - white_text->get_text_width("COINS "), 0),
725 context.draw_text(gold_text, str,
726 Vector(screen->w - gold_text->get_text_width("99"), 0),LAYER_FOREGROUND1);
728 context.draw_text(white_text, "LIVES",
729 Vector(screen->w - white_text->get_text_width("LIVES "), 20),
731 if (player_status.lives >= 5)
733 sprintf(str, "%dx", player_status.lives);
734 float x = screen->w - gold_text->get_text_width(str) - tux_life->w;
735 context.draw_text(gold_text, str, Vector(x, 20), LAYER_FOREGROUND1);
736 context.draw_surface(tux_life, Vector(screen->w - 16, 20),
741 for(int i= 0; i < player_status.lives; ++i)
742 context.draw_surface(tux_life,
743 Vector(screen->w - tux_life->w*4 +(tux_life->w*i), 20),
749 sprintf(str, "%2.1f", fps_fps);
750 context.draw_text(white_text, "FPS",
751 Vector(screen->w - white_text->get_text_width("FPS "), 40),
753 context.draw_text(gold_text, str,
754 Vector(screen->w-4*16, 40), LAYER_FOREGROUND1);
759 GameSession::drawresultscreen(void)
763 DrawingContext context;
764 currentsector->background->draw(context);
766 context.draw_text_center(blue_text, "Result:", Vector(0, 200),
769 sprintf(str, "SCORE: %d", player_status.score);
770 context.draw_text_center(gold_text, str, Vector(0, 224), LAYER_FOREGROUND1);
772 sprintf(str, "COINS: %d", player_status.distros);
773 context.draw_text_center(gold_text, str, Vector(0, 256), LAYER_FOREGROUND1);
775 context.do_drawing();
778 wait_for_event(event,2000,5000,true);
781 std::string slotinfo(int slot)
786 sprintf(slotfile,"%s/slot%d.stsg",st_save_dir,slot);
788 lisp_object_t* savegame = lisp_read_from_file(slotfile);
791 LispReader reader(lisp_cdr(savegame));
792 reader.read_string("title", title);
796 if (access(slotfile, F_OK) == 0)
799 snprintf(tmp,1024,"Slot %d - %s",slot, title.c_str());
801 snprintf(tmp, 1024,"Slot %d - Savegame",slot);
804 sprintf(tmp,"Slot %d - Free",slot);