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 int x = screen->h / 20;
502 for(int i = 0; i < x; ++i)
504 context.draw_filled_rect(
505 Vector(i % 2 ? (pause_menu_frame * i)%screen->w :
506 -((pause_menu_frame * i)%screen->w)
507 ,(i*20+pause_menu_frame)%screen->h),
508 Vector(screen->w,10),
509 Color(20,20,20, rand() % 20 + 1), LAYER_FOREGROUND1+1);
511 context.draw_filled_rect(
512 Vector(0,0), Vector(screen->w, screen->h),
513 Color(rand() % 50, rand() % 50, rand() % 50, 128), LAYER_FOREGROUND1);
514 world->context.draw_text_center(blue_text, "PAUSE - Press 'P' To Play",
515 Vector(0, 230), LAYER_FOREGROUND1+2);
520 Menu::current()->draw(context);
521 mouse_cursor->draw(context);
524 context.do_drawing();
528 GameSession::process_menu()
530 Menu* menu = Menu::current();
535 if(menu == game_menu)
537 switch (game_menu->check())
540 st_pause_ticks_stop();
542 case MNID_ABORTLEVEL:
543 st_pause_ticks_stop();
544 exit_status = ES_LEVEL_ABORT;
548 else if(menu == options_menu)
550 process_options_menu();
552 else if(menu == load_game_menu )
554 process_load_game_menu();
559 GameSession::ExitStatus
562 Menu::set_current(0);
567 update_time = last_update_time = st_get_ticks();
569 // Eat unneeded events
571 while (SDL_PollEvent(&event)) {}
575 while (exit_status == ES_NONE)
577 /* Calculate the movement-factor */
578 double frame_ratio = ((double)(update_time-last_update_time))/((double)FRAME_RATE);
580 if(!frame_timer.check())
582 frame_timer.start(25);
583 ++global_frame_counter;
587 world->get_tux()->input.old_fire = world->get_tux()->input.fire;
592 // Update the world state and all objects in the world
593 // Do that with a constante time-delta so that the game will run
594 // determistic and not different on different machines
595 if(!game_pause && !Menu::current())
598 check_end_conditions();
599 if (end_sequence == ENDSEQUENCE_RUNNING)
600 action(frame_ratio/2);
601 else if(end_sequence == NO_ENDSEQUENCE)
612 /* Time stops in pause mode */
613 if(game_pause || Menu::current())
618 /* Set the time of the last update and the time of the current update */
619 last_update_time = update_time;
620 update_time = st_get_ticks();
622 /* Pause till next frame, if the machine running the game is too fast: */
623 /* FIXME: Works great for in OpenGl mode, where the CPU doesn't have to do that much. But
624 the results in SDL mode aren't perfect (thought the 100 FPS are reached), even on an AMD2500+. */
625 if(last_update_time >= update_time - 12)
628 update_time = st_get_ticks();
632 if (!time_left.check() && world->get_tux()->dying == DYING_NOT
634 world->get_tux()->kill(Player::KILL);
637 if(world->get_tux()->invincible_timer.check() && !end_sequence)
639 world->play_music(HERRING_MUSIC);
641 /* are we low on time ? */
642 else if (time_left.get_left() < TIME_WARNING && !end_sequence)
644 world->play_music(HURRYUP_MUSIC);
646 /* or just normal music? */
647 else if(world->get_music_type() != LEVEL_MUSIC && !end_sequence)
649 world->play_music(LEVEL_MUSIC);
652 /* Calculate frames per second */
656 fps_fps = (1000.0 / (float)fps_timer.get_gone()) * (float)fps_cnt;
658 if(!fps_timer.check())
660 fps_timer.start(1000);
669 /* Bounce a brick: */
670 void bumpbrick(float x, float y)
672 World::current()->add_bouncy_brick(Vector(((int)(x + 1) / 32) * 32,
673 (int)(y / 32) * 32));
675 play_sound(sounds[SND_BRICK], SOUND_CENTER_SPEAKER);
680 GameSession::drawstatus(DrawingContext& context)
682 context.push_transform();
683 context.set_translation(Vector(0, 0));
687 snprintf(str, 60, "%d", player_status.score);
688 context.draw_text(white_text, "SCORE", Vector(0, 0), LAYER_FOREGROUND1);
689 context.draw_text(gold_text, str, Vector(96, 0), LAYER_FOREGROUND1);
691 if(st_gl_mode == ST_GL_TEST)
693 context.draw_text(white_text, "Press ESC To Return", Vector(0,20),
697 if(!time_left.check()) {
698 context.draw_text_center(white_text, "TIME's UP", Vector(0, 0),
700 } else if (time_left.get_left() > TIME_WARNING || (global_frame_counter % 10) < 5) {
701 sprintf(str, "%d", time_left.get_left() / 1000 );
702 context.draw_text_center(white_text, "TIME",
703 Vector(0, 0), LAYER_FOREGROUND1);
704 context.draw_text_center(gold_text, str,
705 Vector(4*16, 0), LAYER_FOREGROUND1);
708 sprintf(str, "%d", player_status.distros);
709 context.draw_text(white_text, "COINS",
710 Vector(screen->w - white_text->w*9, 0), LAYER_FOREGROUND1);
711 context.draw_text(gold_text, str,
712 Vector(screen->w - gold_text->w*2, 0), LAYER_FOREGROUND1);
714 context.draw_text(white_text, "LIVES",
715 Vector(screen->w - white_text->w*9, 20), LAYER_FOREGROUND1);
716 if (player_status.lives >= 5)
718 sprintf(str, "%dx", player_status.lives);
719 float x = screen->w - gold_text->get_text_width(str) - tux_life->w;
720 context.draw_text(gold_text, str, Vector(x, 20), LAYER_FOREGROUND1);
721 context.draw_surface(tux_life, Vector(screen->w - 16, 20),
726 for(int i= 0; i < player_status.lives; ++i)
727 context.draw_surface(tux_life,
728 Vector(screen->w - tux_life->w*4 +(tux_life->w*i), 20),
734 sprintf(str, "%2.1f", fps_fps);
735 context.draw_text(white_text, "FPS",
736 Vector(screen->w - white_text->w*9, 40), LAYER_FOREGROUND1);
737 context.draw_text(gold_text, str,
738 Vector(screen->w-4*16, 40), LAYER_FOREGROUND1);
741 context.pop_transform();
745 GameSession::drawresultscreen(void)
749 DrawingContext context;
750 world->background->draw(context);
752 context.draw_text_center(blue_text, "Result:", Vector(0, 200),
755 sprintf(str, "SCORE: %d", player_status.score);
756 context.draw_text_center(gold_text, str, Vector(0, 224), LAYER_FOREGROUND1);
758 sprintf(str, "COINS: %d", player_status.distros);
759 context.draw_text_center(gold_text, str, Vector(0, 256), LAYER_FOREGROUND1);
761 context.do_drawing();
764 wait_for_event(event,2000,5000,true);
767 std::string slotinfo(int slot)
772 sprintf(slotfile,"%s/slot%d.stsg",st_save_dir,slot);
774 lisp_object_t* savegame = lisp_read_from_file(slotfile);
777 LispReader reader(lisp_cdr(savegame));
778 reader.read_string("title", &title);
782 if (access(slotfile, F_OK) == 0)
785 snprintf(tmp,1024,"Slot %d - %s",slot, title.c_str());
787 snprintf(tmp, 1024,"Slot %d - Savegame",slot);
790 sprintf(tmp,"Slot %d - Free",slot);