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"
57 #include "music_manager.h"
59 GameSession* GameSession::current_ = 0;
61 GameSession::GameSession(const std::string& subset_, int levelnb_, int mode)
62 : world(0), st_gl_mode(mode), levelnb(levelnb_), end_sequence(NO_ENDSEQUENCE),
67 global_frame_counter = 0;
72 frame_timer.init(true);
78 GameSession::restart_level()
81 exit_status = ES_NONE;
82 end_sequence = NO_ENDSEQUENCE;
85 frame_timer.init(true);
90 { // Tux has lost a life, so we try to respawn him at the nearest reset point
91 old_x_pos = world->get_tux()->base.x;
96 if (st_gl_mode == ST_GL_LOAD_LEVEL_FILE)
98 world = new World(subset);
100 else if (st_gl_mode == ST_GL_DEMO_GAME)
102 world = new World(subset);
106 world = new World(subset, levelnb);
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;
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 world->play_music(LEVEL_MUSIC);
138 GameSession::~GameSession()
144 GameSession::levelintro(void)
146 music_manager->halt_music();
150 DrawingContext context;
151 world->background->draw(context);
153 sprintf(str, "%s", world->get_level()->name.c_str());
154 context.draw_text_center(gold_text, str, Vector(0, 220), 0);
156 sprintf(str, "TUX x %d", player_status.lives);
157 context.draw_text_center(white_text, str, Vector(0, 240), 0);
159 sprintf(str, "by %s", world->get_level()->author.c_str());
160 context.draw_text_center(white_small_text, str, Vector(0, 400), 0);
162 context.do_drawing();
165 wait_for_event(event,1000,3000,true);
170 GameSession::start_timers()
172 time_left.start(world->get_level()->time_left*1000);
173 st_pause_ticks_init();
174 update_time = st_get_ticks();
178 GameSession::on_escape_press()
180 if(world->get_tux()->dying || end_sequence != NO_ENDSEQUENCE)
181 return; // don't let the player open the menu, when he is dying
185 if(st_gl_mode == ST_GL_TEST)
187 exit_status = ES_LEVEL_ABORT;
189 else if (!Menu::current())
191 /* Tell Tux that the keys are all down, otherwise
192 it could have nasty bugs, like going allways to the right
193 or whatever that key does */
194 Player& tux = *world->get_tux();
195 tux.key_event((SDLKey)keymap.jump, UP);
196 tux.key_event((SDLKey)keymap.duck, UP);
197 tux.key_event((SDLKey)keymap.left, UP);
198 tux.key_event((SDLKey)keymap.right, UP);
199 tux.key_event((SDLKey)keymap.fire, UP);
201 Menu::set_current(game_menu);
202 st_pause_ticks_start();
207 GameSession::process_events()
209 if (end_sequence != NO_ENDSEQUENCE)
211 Player& tux = *world->get_tux();
215 tux.input.right = DOWN;
218 if (int(last_x_pos) == int(tux.base.x))
223 last_x_pos = tux.base.x;
226 while (SDL_PollEvent(&event))
228 /* Check for menu-events, if the menu is shown */
231 Menu::current()->event(event);
233 st_pause_ticks_stop();
238 case SDL_QUIT: /* Quit event - quit: */
239 st_abort("Received window close", "");
242 case SDL_KEYDOWN: /* A keypress! */
244 SDLKey key = event.key.keysym.sym;
248 case SDLK_ESCAPE: /* Escape: Open/Close the menu: */
256 case SDL_JOYBUTTONDOWN:
257 if (event.jbutton.button == joystick_keymap.start_button)
265 if(!Menu::current() && !game_pause)
266 st_pause_ticks_stop();
269 while (SDL_PollEvent(&event))
271 /* Check for menu-events, if the menu is shown */
274 Menu::current()->event(event);
276 st_pause_ticks_stop();
280 Player& tux = *world->get_tux();
284 case SDL_QUIT: /* Quit event - quit: */
285 st_abort("Received window close", "");
288 case SDL_KEYDOWN: /* A keypress! */
290 SDLKey key = event.key.keysym.sym;
292 if(tux.key_event(key,DOWN))
297 case SDLK_ESCAPE: /* Escape: Open/Close the menu: */
305 case SDL_KEYUP: /* A keyrelease! */
307 SDLKey key = event.key.keysym.sym;
309 if(tux.key_event(key, UP))
320 st_pause_ticks_stop();
325 st_pause_ticks_start();
332 tux.size = !tux.size;
335 tux.base.height = 64;
338 tux.base.height = 32;
343 player_status.distros += 50;
347 tux.got_power = tux.FIRE_POWER;
351 tux.got_power = tux.ICE_POWER;
355 tux.invincible_timer.start(TUX_INVINCIBLE_TIME);
359 --player_status.lives;
363 player_status.score += 1000;
376 case SDL_JOYAXISMOTION:
377 if (event.jaxis.axis == joystick_keymap.x_axis)
379 if (event.jaxis.value < -joystick_keymap.dead_zone)
381 tux.input.left = DOWN;
382 tux.input.right = UP;
384 else if (event.jaxis.value > joystick_keymap.dead_zone)
387 tux.input.right = DOWN;
391 tux.input.left = DOWN;
392 tux.input.right = DOWN;
395 else if (event.jaxis.axis == joystick_keymap.y_axis)
397 if (event.jaxis.value > joystick_keymap.dead_zone)
398 tux.input.down = DOWN;
399 else if (event.jaxis.value < -joystick_keymap.dead_zone)
406 case SDL_JOYBUTTONDOWN:
407 if (event.jbutton.button == joystick_keymap.a_button)
409 else if (event.jbutton.button == joystick_keymap.b_button)
410 tux.input.fire = DOWN;
411 else if (event.jbutton.button == joystick_keymap.start_button)
414 case SDL_JOYBUTTONUP:
415 if (event.jbutton.button == joystick_keymap.a_button)
417 else if (event.jbutton.button == joystick_keymap.b_button)
430 GameSession::check_end_conditions()
432 Player* tux = world->get_tux();
435 int endpos = (World::current()->get_level()->width-5) * 32;
436 Tile* endtile = collision_goal(tux->base);
438 // fallback in case the other endpositions don't trigger
439 if (!end_sequence && tux->base.x >= endpos)
441 end_sequence = ENDSEQUENCE_WAITING;
443 music_manager->play_music(level_end_song, 0);
444 endsequence_timer.start(7000);
445 tux->invincible_timer.start(7000); //FIXME: Implement a winning timer for the end sequence (with special winning animation etc.)
447 else if(end_sequence && !endsequence_timer.check())
449 exit_status = ES_LEVEL_FINISHED;
452 else if(end_sequence == ENDSEQUENCE_RUNNING && endtile && endtile->data >= 1)
454 end_sequence = ENDSEQUENCE_WAITING;
456 else if(!end_sequence && endtile && endtile->data == 0)
458 end_sequence = ENDSEQUENCE_RUNNING;
460 music_manager->play_music(level_end_song, 0);
461 endsequence_timer.start(7000); // 5 seconds until we finish the map
462 tux->invincible_timer.start(7000); //FIXME: Implement a winning timer for the end sequence (with special winning animation etc.)
464 else if (!end_sequence && tux->is_dead())
466 player_status.bonus = PlayerStatus::NO_BONUS;
468 if (player_status.lives < 0)
470 exit_status = ES_GAME_OVER;
473 { // Still has lives, so reset Tux to the levelstart
482 GameSession::action(double frame_ratio)
484 if (exit_status == ES_NONE && !world->get_tux()->growing_timer.check())
486 // Update Tux and the World
487 world->action(frame_ratio);
494 DrawingContext& context = world->context;
501 context.push_transform();
502 context.set_translation(Vector(0, 0));
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 world->context.draw_text_center(blue_text, "PAUSE - Press 'P' To Play",
518 Vector(0, 230), LAYER_FOREGROUND1+2);
520 context.pop_transform();
525 Menu::current()->draw(context);
526 mouse_cursor->draw(context);
529 context.do_drawing();
533 GameSession::process_menu()
535 Menu* menu = Menu::current();
540 if(menu == game_menu)
542 switch (game_menu->check())
545 st_pause_ticks_stop();
547 case MNID_ABORTLEVEL:
548 st_pause_ticks_stop();
549 exit_status = ES_LEVEL_ABORT;
553 else if(menu == options_menu)
555 process_options_menu();
557 else if(menu == load_game_menu )
559 process_load_game_menu();
564 GameSession::ExitStatus
567 Menu::set_current(0);
572 update_time = last_update_time = st_get_ticks();
574 // Eat unneeded events
576 while (SDL_PollEvent(&event)) {}
580 while (exit_status == ES_NONE)
582 /* Calculate the movement-factor */
583 double frame_ratio = ((double)(update_time-last_update_time))/((double)FRAME_RATE);
585 if(!frame_timer.check())
587 frame_timer.start(25);
588 ++global_frame_counter;
592 world->get_tux()->input.old_fire = world->get_tux()->input.fire;
597 // Update the world state and all objects in the world
598 // Do that with a constante time-delta so that the game will run
599 // determistic and not different on different machines
600 if(!game_pause && !Menu::current())
603 check_end_conditions();
604 if (end_sequence == ENDSEQUENCE_RUNNING)
605 action(frame_ratio/2);
606 else if(end_sequence == NO_ENDSEQUENCE)
617 /* Time stops in pause mode */
618 if(game_pause || Menu::current())
623 /* Set the time of the last update and the time of the current update */
624 last_update_time = update_time;
625 update_time = st_get_ticks();
627 /* Pause till next frame, if the machine running the game is too fast: */
628 /* FIXME: Works great for in OpenGl mode, where the CPU doesn't have to do that much. But
629 the results in SDL mode aren't perfect (thought the 100 FPS are reached), even on an AMD2500+. */
630 if(last_update_time >= update_time - 12)
633 update_time = st_get_ticks();
637 if (!time_left.check() && world->get_tux()->dying == DYING_NOT
639 world->get_tux()->kill(Player::KILL);
642 if(world->get_tux()->invincible_timer.check() && !end_sequence)
644 world->play_music(HERRING_MUSIC);
646 /* are we low on time ? */
647 else if (time_left.get_left() < TIME_WARNING && !end_sequence)
649 world->play_music(HURRYUP_MUSIC);
651 /* or just normal music? */
652 else if(world->get_music_type() != LEVEL_MUSIC && !end_sequence)
654 world->play_music(LEVEL_MUSIC);
657 /* Calculate frames per second */
661 fps_fps = (1000.0 / (float)fps_timer.get_gone()) * (float)fps_cnt;
663 if(!fps_timer.check())
665 fps_timer.start(1000);
674 /* Bounce a brick: */
675 void bumpbrick(float x, float y)
677 World::current()->add_bouncy_brick(Vector(((int)(x + 1) / 32) * 32,
678 (int)(y / 32) * 32));
680 play_sound(sounds[SND_BRICK], SOUND_CENTER_SPEAKER);
685 GameSession::drawstatus(DrawingContext& context)
687 context.push_transform();
688 context.set_translation(Vector(0, 0));
692 snprintf(str, 60, "%d", player_status.score);
693 context.draw_text(white_text, "SCORE", Vector(0, 0), LAYER_FOREGROUND1);
694 context.draw_text(gold_text, str, Vector(96, 0), LAYER_FOREGROUND1);
696 if(st_gl_mode == ST_GL_TEST)
698 context.draw_text(white_text, "Press ESC To Return", Vector(0,20),
702 if(!time_left.check()) {
703 context.draw_text_center(white_text, "TIME's UP", Vector(0, 0),
705 } else if (time_left.get_left() > TIME_WARNING || (global_frame_counter % 10) < 5) {
706 sprintf(str, "%d", time_left.get_left() / 1000 );
707 context.draw_text_center(white_text, "TIME",
708 Vector(0, 0), LAYER_FOREGROUND1);
709 context.draw_text_center(gold_text, str,
710 Vector(4*16, 0), LAYER_FOREGROUND1);
713 sprintf(str, "%d", player_status.distros);
714 context.draw_text(white_text, "COINS",
715 Vector(screen->w - white_text->w*9, 0), LAYER_FOREGROUND1);
716 context.draw_text(gold_text, str,
717 Vector(screen->w - gold_text->w*2, 0), LAYER_FOREGROUND1);
719 context.draw_text(white_text, "LIVES",
720 Vector(screen->w - white_text->w*9, 20), LAYER_FOREGROUND1);
721 if (player_status.lives >= 5)
723 sprintf(str, "%dx", player_status.lives);
724 float x = screen->w - gold_text->get_text_width(str) - tux_life->w;
725 context.draw_text(gold_text, str, Vector(x, 20), LAYER_FOREGROUND1);
726 context.draw_surface(tux_life, Vector(screen->w - 16, 20),
731 for(int i= 0; i < player_status.lives; ++i)
732 context.draw_surface(tux_life,
733 Vector(screen->w - tux_life->w*4 +(tux_life->w*i), 20),
739 sprintf(str, "%2.1f", fps_fps);
740 context.draw_text(white_text, "FPS",
741 Vector(screen->w - white_text->w*9, 40), LAYER_FOREGROUND1);
742 context.draw_text(gold_text, str,
743 Vector(screen->w-4*16, 40), LAYER_FOREGROUND1);
746 context.pop_transform();
750 GameSession::drawresultscreen(void)
754 DrawingContext context;
755 world->background->draw(context);
757 context.draw_text_center(blue_text, "Result:", Vector(0, 200),
760 sprintf(str, "SCORE: %d", player_status.score);
761 context.draw_text_center(gold_text, str, Vector(0, 224), LAYER_FOREGROUND1);
763 sprintf(str, "COINS: %d", player_status.distros);
764 context.draw_text_center(gold_text, str, Vector(0, 256), LAYER_FOREGROUND1);
766 context.do_drawing();
769 wait_for_event(event,2000,5000,true);
772 std::string slotinfo(int slot)
777 sprintf(slotfile,"%s/slot%d.stsg",st_save_dir,slot);
779 lisp_object_t* savegame = lisp_read_from_file(slotfile);
782 LispReader reader(lisp_cdr(savegame));
783 reader.read_string("title", &title);
787 if (access(slotfile, F_OK) == 0)
790 snprintf(tmp,1024,"Slot %d - %s",slot, title.c_str());
792 snprintf(tmp, 1024,"Slot %d - Savegame",slot);
795 sprintf(tmp,"Slot %d - Free",slot);