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>
44 #include "high_scores.h"
52 #include "collision.h"
54 #include "particlesystem.h"
55 #include "resources.h"
56 #include "music_manager.h"
58 GameSession* GameSession::current_ = 0;
60 GameSession::GameSession(const std::string& subset_, int levelnb_, int mode)
61 : world(0), st_gl_mode(mode), levelnb(levelnb_), end_sequence(false),
66 global_frame_counter = 0;
70 frame_timer.init(true);
76 GameSession::restart_level()
83 frame_timer.init(true);
88 { // Tux has lost a life, so we try to respawn him at the nearest reset point
89 old_x_pos = world->get_tux()->base.x;
94 if (st_gl_mode == ST_GL_LOAD_LEVEL_FILE)
96 world = new World(subset);
98 else if (st_gl_mode == ST_GL_DEMO_GAME)
100 world = new World(subset);
104 world = new World(subset, levelnb);
107 // Set Tux to the nearest reset point
110 ResetPoint best_reset_point = { -1, -1 };
111 for(std::vector<ResetPoint>::iterator i = get_level()->reset_points.begin();
112 i != get_level()->reset_points.end(); ++i)
114 if (i->x < old_x_pos && best_reset_point.x < i->x)
115 best_reset_point = *i;
118 if (best_reset_point.x != -1)
120 world->get_tux()->base.x = best_reset_point.x;
121 world->get_tux()->base.y = best_reset_point.y;
125 if (st_gl_mode != ST_GL_DEMO_GAME)
127 if(st_gl_mode == ST_GL_PLAY || st_gl_mode == ST_GL_LOAD_LEVEL_FILE)
131 time_left.init(true);
133 world->play_music(LEVEL_MUSIC);
136 GameSession::~GameSession()
142 GameSession::levelintro(void)
144 music_manager->halt_music();
148 if (get_level()->img_bkgd)
149 get_level()->img_bkgd->draw(0, 0);
151 drawgradient(get_level()->bkgd_top, get_level()->bkgd_bottom);
153 sprintf(str, "%s", world->get_level()->name.c_str());
154 gold_text->drawf(str, 0, 200, A_HMIDDLE, A_TOP, 1);
156 sprintf(str, "TUX x %d", player_status.lives);
157 white_text->drawf(str, 0, 224, A_HMIDDLE, A_TOP, 1);
159 sprintf(str, "by %s", world->get_level()->author.c_str());
160 white_small_text->drawf(str, 0, 360, A_HMIDDLE, A_TOP, 1);
166 wait_for_event(event,1000,3000,true);
171 GameSession::start_timers()
173 time_left.start(world->get_level()->time_left*1000);
174 st_pause_ticks_init();
175 update_time = st_get_ticks();
179 GameSession::on_escape_press()
184 if(st_gl_mode == ST_GL_TEST)
186 exit_status = LEVEL_ABORT;
188 else if (!Menu::current())
190 Menu::set_current(game_menu);
195 GameSession::process_events()
199 Player& tux = *world->get_tux();
202 tux.input.right = DOWN;
205 if (int(last_x_pos) == int(tux.base.x))
210 last_x_pos = tux.base.x;
213 while (SDL_PollEvent(&event))
215 /* Check for menu-events, if the menu is shown */
218 Menu::current()->event(event);
219 st_pause_ticks_start();
224 case SDL_QUIT: /* Quit event - quit: */
225 st_abort("Received window close", "");
228 case SDL_KEYDOWN: /* A keypress! */
230 SDLKey key = event.key.keysym.sym;
234 case SDLK_ESCAPE: /* Escape: Open/Close the menu: */
242 case SDL_JOYBUTTONDOWN:
243 if (event.jbutton.button == joystick_keymap.start_button)
251 if(!Menu::current() && !game_pause)
252 st_pause_ticks_stop();
255 while (SDL_PollEvent(&event))
257 /* Check for menu-events, if the menu is shown */
260 Menu::current()->event(event);
261 st_pause_ticks_start();
265 Player& tux = *world->get_tux();
269 case SDL_QUIT: /* Quit event - quit: */
270 st_abort("Received window close", "");
273 case SDL_KEYDOWN: /* A keypress! */
275 SDLKey key = event.key.keysym.sym;
277 if(tux.key_event(key,DOWN))
282 case SDLK_ESCAPE: /* Escape: Open/Close the menu: */
290 case SDL_KEYUP: /* A keyrelease! */
292 SDLKey key = event.key.keysym.sym;
294 if(tux.key_event(key, UP))
305 st_pause_ticks_stop();
310 st_pause_ticks_start();
317 tux.size = !tux.size;
320 tux.base.height = 64;
323 tux.base.height = 32;
328 player_status.distros += 50;
336 tux.invincible_timer.start(TUX_INVINCIBLE_TIME);
340 --player_status.lives;
344 player_status.score += 1000;
357 case SDL_JOYAXISMOTION:
358 if (event.jaxis.axis == joystick_keymap.x_axis)
360 if (event.jaxis.value < -joystick_keymap.dead_zone)
362 tux.input.left = DOWN;
363 tux.input.right = UP;
365 else if (event.jaxis.value > joystick_keymap.dead_zone)
368 tux.input.right = DOWN;
372 tux.input.left = DOWN;
373 tux.input.right = DOWN;
376 else if (event.jaxis.axis == joystick_keymap.y_axis)
378 if (event.jaxis.value > joystick_keymap.dead_zone)
379 tux.input.down = DOWN;
380 else if (event.jaxis.value < -joystick_keymap.dead_zone)
387 case SDL_JOYBUTTONDOWN:
388 if (event.jbutton.button == joystick_keymap.a_button)
390 else if (event.jbutton.button == joystick_keymap.b_button)
391 tux.input.fire = DOWN;
392 else if (event.jbutton.button == joystick_keymap.start_button)
395 case SDL_JOYBUTTONUP:
396 if (event.jbutton.button == joystick_keymap.a_button)
398 else if (event.jbutton.button == joystick_keymap.b_button)
411 GameSession::check_end_conditions()
413 Player* tux = world->get_tux();
416 int endpos = (World::current()->get_level()->width-5) * 32;
417 Tile* endtile = collision_goal(tux->base);
419 // fallback in case the other endpositions don't trigger
420 if (tux->base.x >= endpos || (endtile && endtile->data >= 1)
421 || (end_sequence && !endsequence_timer.check()))
423 exit_status = LEVEL_FINISHED;
426 else if(!end_sequence && endtile && endtile->data == 0)
430 music_manager->halt_music();
431 endsequence_timer.start(5000); // 5 seconds until we finish the map
433 else if (!end_sequence && tux->is_dead())
435 player_status.bonus = PlayerStatus::NO_BONUS;
436 player_status.lives -= 1;
438 if (player_status.lives < 0)
440 if(st_gl_mode != ST_GL_TEST)
443 exit_status = GAME_OVER;
446 { // Still has lives, so reset Tux to the levelstart
455 GameSession::action(double frame_ratio)
457 check_end_conditions();
459 if (exit_status == NONE)
461 // Update Tux and the World
462 world->action(frame_ratio);
474 int x = screen->h / 20;
475 for(int i = 0; i < x; ++i)
477 fillrect(i % 2 ? (pause_menu_frame * i)%screen->w : -((pause_menu_frame * i)%screen->w) ,(i*20+pause_menu_frame)%screen->h,screen->w,10,20,20,20, rand() % 20 + 1);
479 fillrect(0,0,screen->w,screen->h,rand() % 50, rand() % 50, rand() % 50, 128);
480 blue_text->drawf("PAUSE - Press 'P' To Play", 0, 230, A_HMIDDLE, A_TOP, 1);
485 Menu::current()->draw();
486 mouse_cursor->draw();
493 GameSession::process_menu()
495 Menu* menu = Menu::current();
500 if(menu == game_menu)
502 switch (game_menu->check())
505 st_pause_ticks_stop();
507 case MNID_ABORTLEVEL:
508 st_pause_ticks_stop();
509 exit_status = LEVEL_ABORT;
513 else if(menu == options_menu)
515 process_options_menu();
517 else if(menu == load_game_menu )
519 process_load_game_menu();
524 GameSession::ExitStatus
527 Menu::set_current(0);
532 update_time = last_update_time = st_get_ticks();
535 // clearscreen(0, 0, 0);
538 // Eat unneeded events
540 while (SDL_PollEvent(&event)) {}
544 float overlap = 0.0f;
545 while (exit_status == NONE)
547 /* Calculate the movement-factor */
548 double frame_ratio = ((double)(update_time-last_update_time))/((double)FRAME_RATE);
550 if(!frame_timer.check())
552 frame_timer.start(25);
553 ++global_frame_counter;
557 world->get_tux()->input.old_fire = world->get_tux()->input.fire;
562 // Update the world state and all objects in the world
563 // Do that with a constante time-delta so that the game will run
564 // determistic and not different on different machines
565 if(!game_pause && !Menu::current())
567 frame_ratio *= game_speed;
568 frame_ratio += overlap;
569 while (frame_ratio > 0)
578 overlap = frame_ratio;
588 /* Time stops in pause mode */
589 if(game_pause || Menu::current())
594 /* Set the time of the last update and the time of the current update */
595 last_update_time = update_time;
596 update_time = st_get_ticks();
598 /* Pause till next frame, if the machine running the game is too fast: */
599 /* FIXME: Works great for in OpenGl mode, where the CPU doesn't have to do that much. But
600 the results in SDL mode aren't perfect (thought the 100 FPS are reached), even on an AMD2500+. */
601 if(last_update_time >= update_time - 12)
604 update_time = st_get_ticks();
608 if (!time_left.check() && world->get_tux()->dying == DYING_NOT)
609 world->get_tux()->kill(Player::KILL);
612 if(world->get_tux()->invincible_timer.check())
614 if(world->get_music_type() != HERRING_MUSIC)
615 world->play_music(HERRING_MUSIC);
617 /* are we low on time ? */
618 else if (time_left.get_left() < TIME_WARNING)
620 world->play_music(HURRYUP_MUSIC);
622 /* or just normal music? */
623 else if(world->get_music_type() != LEVEL_MUSIC)
625 world->play_music(LEVEL_MUSIC);
628 /* Calculate frames per second */
632 fps_fps = (1000.0 / (float)fps_timer.get_gone()) * (float)fps_cnt;
634 if(!fps_timer.check())
636 fps_timer.start(1000);
645 /* Bounce a brick: */
646 void bumpbrick(float x, float y)
648 World::current()->add_bouncy_brick(((int)(x + 1) / 32) * 32,
651 play_sound(sounds[SND_BRICK], SOUND_CENTER_SPEAKER);
656 GameSession::drawstatus()
660 sprintf(str, "%d", player_status.score);
661 white_text->draw("SCORE", 0, 0, 1);
662 gold_text->draw(str, 96, 0, 1);
664 if(st_gl_mode == ST_GL_TEST)
666 white_text->draw("Press ESC To Return",0,20,1);
669 if(!time_left.check()) {
670 white_text->draw("TIME'S UP", 224, 0, 1);
671 } else if (time_left.get_left() > TIME_WARNING || (global_frame_counter % 10) < 5) {
672 sprintf(str, "%d", time_left.get_left() / 1000 );
673 white_text->draw("TIME", 224, 0, 1);
674 gold_text->draw(str, 304, 0, 1);
677 sprintf(str, "%d", player_status.distros);
678 white_text->draw("COINS", screen->h, 0, 1);
679 gold_text->draw(str, 608, 0, 1);
681 white_text->draw("LIVES", 480, 20);
682 if (player_status.lives >= 5)
684 sprintf(str, "%dx", player_status.lives);
685 gold_text->draw(str, 585, 20);
686 tux_life->draw(565+(18*3), 20);
690 for(int i= 0; i < player_status.lives; ++i)
691 tux_life->draw(565+(18*i),20);
696 sprintf(str, "%2.1f", fps_fps);
697 white_text->draw("FPS", screen->h, 40, 1);
698 gold_text->draw(str, screen->h + 60, 40, 1);
703 GameSession::drawendscreen()
707 if (get_level()->img_bkgd)
708 get_level()->img_bkgd->draw(0, 0);
710 drawgradient(get_level()->bkgd_top, get_level()->bkgd_bottom);
712 blue_text->drawf("GAMEOVER", 0, 200, A_HMIDDLE, A_TOP, 1);
714 sprintf(str, "SCORE: %d", player_status.score);
715 gold_text->drawf(str, 0, 224, A_HMIDDLE, A_TOP, 1);
717 sprintf(str, "COINS: %d", player_status.distros);
718 gold_text->drawf(str, 0, 256, A_HMIDDLE, A_TOP, 1);
723 wait_for_event(event,2000,5000,true);
727 GameSession::drawresultscreen(void)
731 if (get_level()->img_bkgd)
732 get_level()->img_bkgd->draw(0, 0);
734 drawgradient(get_level()->bkgd_top, get_level()->bkgd_bottom);
736 blue_text->drawf("Result:", 0, 200, A_HMIDDLE, A_TOP, 1);
738 sprintf(str, "SCORE: %d", player_status.score);
739 gold_text->drawf(str, 0, 224, A_HMIDDLE, A_TOP, 1);
741 sprintf(str, "COINS: %d", player_status.distros);
742 gold_text->drawf(str, 0, 256, A_HMIDDLE, A_TOP, 1);
747 wait_for_event(event,2000,5000,true);
750 std::string slotinfo(int slot)
755 sprintf(slotfile,"%s/slot%d.stsg",st_save_dir,slot);
757 lisp_object_t* savegame = lisp_read_from_file(slotfile);
760 LispReader reader(lisp_cdr(savegame));
761 reader.read_string("title", &title);
765 if (access(slotfile, F_OK) == 0)
768 snprintf(tmp,1024,"Slot %d - %s",slot, title.c_str());
770 snprintf(tmp, 1024,"Slot %d - Savegame",slot);
773 sprintf(tmp,"Slot %d - Free",slot);