2 // C Implementation: player/tux
7 // Author: Tobias Glaesser <tobi.web@gmx.de> & Bill Kendrick, (C) 2004
9 // Copyright: See COPYING file that comes with this distribution
23 std::vector<Surface*> tux_right;
24 std::vector<Surface*> tux_left;
25 Surface* smalltux_jump_left;
26 Surface* smalltux_jump_right;
27 Surface* smalltux_stand_left;
28 Surface* smalltux_stand_right;
30 Surface* bigtux_right[3];
31 Surface* bigtux_left[3];
32 Surface* bigtux_right_jump;
33 Surface* bigtux_left_jump;
34 Surface* ducktux_right;
35 Surface* ducktux_left;
36 Surface* skidtux_right;
37 Surface* skidtux_left;
38 Surface* firetux_right[3];
39 Surface* firetux_left[3];
40 Surface* bigfiretux_right[3];
41 Surface* bigfiretux_left[3];
42 Surface* bigfiretux_right_jump;
43 Surface* bigfiretux_left_jump;
44 Surface* duckfiretux_right;
45 Surface* duckfiretux_left;
46 Surface* skidfiretux_right;
47 Surface* skidfiretux_left;
48 Surface* cape_right[2];
49 Surface* cape_left[2];
50 Surface* bigcape_right[2];
51 Surface* bigcape_left[2];
53 void player_input_init(player_input_type* pplayer_input)
55 pplayer_input->down = UP;
56 pplayer_input->fire = UP;
57 pplayer_input->left = UP;
58 pplayer_input->old_fire = UP;
59 pplayer_input->right = UP;
60 pplayer_input->up = UP;
72 // FIXME: Make the start position configurable via the levelfile
77 previous_base = old_base = base;
90 player_input_init(&input);
92 keymap.jump = SDLK_UP;
93 keymap.duck = SDLK_DOWN;
94 keymap.left = SDLK_LEFT;
95 keymap.right = SDLK_RIGHT;
96 keymap.fire = SDLK_LCTRL;
98 invincible_timer.init(true);
99 skidding_timer.init(true);
100 safe_timer.init(true);
101 frame_timer.init(true);
107 Player::key_event(SDLKey key, int state)
109 if(key == keymap.right)
114 else if(key == keymap.left)
119 else if(key == keymap.jump)
124 else if(key == keymap.duck)
129 else if(key == keymap.fire)
139 Player::level_begin()
145 previous_base = old_base = base;
150 player_input_init(&input);
152 invincible_timer.init(true);
153 skidding_timer.init(true);
154 safe_timer.init(true);
155 frame_timer.init(true);
161 Player::action(double frame_ratio)
163 bool jumped_in_solid = false;
165 /* --- HANDLE TUX! --- */
167 if(dying == DYING_NOT)
171 previous_base = base;
173 physic.apply(frame_ratio, base.x, base.y);
174 if(dying == DYING_NOT) {
175 collision_swept_object_map(&old_base, &base);
176 // special exception for cases where we're stuck under tiles after
177 // being ducked. In this case we drift out
178 if(!duck && on_ground() && old_base.x == base.x && old_base.y == base.y
179 && collision_object_map(&base)) {
180 base.x += frame_ratio * WALK_SPEED * (dir ? 1 : -1);
181 previous_base = old_base = base;
186 if (dying == DYING_NOT)
193 physic.enable_gravity(true);
197 physic.set_velocity(physic.get_velocity_x(), 0);
198 jumped_in_solid = true;
204 if (physic.get_velocity_y() < 0)
206 base.y = (int)(((int)base.y / 32) * 32);
207 physic.set_velocity(physic.get_velocity_x(), 0);
210 physic.enable_gravity(false);
211 /* Reset score multiplier (for multi-hits): */
212 player_status.score_multiplier = 1;
217 if (isbrick(base.x, base.y) ||
218 isfullbox(base.x, base.y))
220 World::current()->trygrabdistro(base.x, base.y - 32,BOUNCE);
221 World::current()->trybumpbadguy(base.x, base.y - 64);
223 World::current()->trybreakbrick(base.x, base.y, size == SMALL);
225 bumpbrick(base.x, base.y);
226 World::current()->tryemptybox(base.x, base.y, RIGHT);
229 if (isbrick(base.x+ 31, base.y) ||
230 isfullbox(base.x+ 31, base.y))
232 World::current()->trygrabdistro(base.x+ 31, base.y - 32,BOUNCE);
233 World::current()->trybumpbadguy(base.x+ 31, base.y - 64);
236 World::current()->trybreakbrick(base.x+ 31, base.y, size == SMALL);
238 bumpbrick(base.x+ 31, base.y);
239 World::current()->tryemptybox(base.x+ 31, base.y, LEFT);
251 /* Make sure jumping is off. */
258 /* ---- DONE HANDLING TUX! --- */
260 /* Handle invincibility timer: */
261 if (get_current_music() == HERRING_MUSIC && !invincible_timer.check())
264 no, we are no more invincible
265 or we were not in invincible mode
266 but are we in hurry ?
269 // FIXME: Move this to gamesession
270 if (GameSession::current()->time_left.get_left() < TIME_WARNING)
272 /* yes, we are in hurry
273 stop the herring_song, prepare to play the correct
276 set_current_music(HURRYUP_MUSIC);
280 set_current_music(LEVEL_MUSIC);
283 /* start playing it */
284 play_current_music();
288 if (base.x >= World::current()->get_level()->endpos
289 && World::current()->get_level()->endpos != 0)
291 player_status.next_level = 1;
295 skidding_timer.check();
296 invincible_timer.check();
303 return ( issolid(base.x + base.width / 2, base.y + base.height) ||
304 issolid(base.x + 1, base.y + base.height) ||
305 issolid(base.x + base.width - 1, base.y + base.height) );
309 Player::under_solid()
311 return ( issolid(base.x + base.width / 2, base.y) ||
312 issolid(base.x + 1, base.y) ||
313 issolid(base.x + base.width - 1, base.y) );
317 Player::handle_horizontal_input()
319 float vx = physic.get_velocity_x();
320 float vy = physic.get_velocity_y();
321 float ax = physic.get_acceleration_x();
322 float ay = physic.get_acceleration_y();
325 if(!duck && input.left == DOWN && input.right == UP) {
328 } else if(!duck && input.left == UP && input.right == DOWN) {
333 if (input.fire == UP) {
334 ax = dirsign * WALK_ACCELERATION_X;
336 if(vx >= MAX_WALK_XM && dirsign > 0) {
339 } else if(vx <= -MAX_WALK_XM && dirsign < 0) {
344 ax = dirsign * RUN_ACCELERATION_X;
346 if(vx >= MAX_RUN_XM && dirsign > 0) {
349 } else if(vx <= -MAX_RUN_XM && dirsign < 0) {
355 // we can reach WALK_SPEED without any acceleration
356 if(dirsign != 0 && fabs(vx) < WALK_SPEED) {
357 vx = dirsign * WALK_SPEED;
360 // changing directions?
361 if(on_ground() && ((vx < 0 && dirsign >0) || (vx>0 && dirsign<0))) {
362 if(fabs(vx)>SKID_XM && !skidding_timer.check()) {
363 skidding_timer.start(SKID_TIME);
364 play_sound(sounds[SND_SKID], SOUND_CENTER_SPEAKER);
371 // we get slower when not pressing any keys
373 if(fabs(vx) < WALK_SPEED) {
377 ax = WALK_ACCELERATION_X * 1.5;
379 ax = WALK_ACCELERATION_X * -1.5;
383 // if we're on ice slow down acceleration or deceleration
384 if (isice(base.x, base.y + base.height))
386 /* the acceleration/deceleration rate on ice is inversely proportional to
387 * the current velocity.
390 // increasing 1 will increase acceleration/deceleration rate
391 // decreasing 1 will decrease acceleration/deceleration rate
392 // must stay above zero, though
393 if (ax != 0) ax *= 1 / fabs(vx);
396 physic.set_velocity(vx, vy);
397 physic.set_acceleration(ax, ay);
401 Player::handle_vertical_input()
405 if (on_ground() && !duck)
408 physic.set_velocity(physic.get_velocity_x(), 5.5);
412 play_sound(sounds[SND_JUMP], SOUND_CENTER_SPEAKER);
414 play_sound(sounds[SND_BIGJUMP], SOUND_CENTER_SPEAKER);
417 else if(input.up == UP && jumping)
420 if(physic.get_velocity_y() > 0) {
421 physic.set_velocity(physic.get_velocity_x(), 0);
427 Player::handle_input()
429 /* Handle horizontal movement: */
430 handle_horizontal_input();
434 if ( input.up == DOWN || (input.up == UP && jumping))
436 handle_vertical_input();
441 if (input.fire == DOWN && input.old_fire == UP && got_coffee)
443 World::current()->add_bullet(base.x, base.y, physic.get_velocity_x(), dir);
446 /* tux animations: */
447 if(!frame_timer.check())
449 frame_timer.start(25);
450 if (input.right == UP && input.left == UP)
457 if ((input.fire == DOWN && (global_frame_counter % 2) == 0) ||
458 (global_frame_counter % 4) == 0)
459 frame_main = (frame_main + 1) % 4;
469 if (input.down == DOWN && size == BIG && !duck)
474 // changing base size confuses collision otherwise
475 old_base = previous_base = base;
477 else if(input.down == UP && size == BIG && duck)
482 old_base = previous_base = base;
487 Player::grabdistros()
492 World::current()->trygrabdistro(base.x, base.y, NO_BOUNCE);
493 World::current()->trygrabdistro(base.x+ 31, base.y, NO_BOUNCE);
495 World::current()->trygrabdistro(base.x, base.y + base.height, NO_BOUNCE);
496 World::current()->trygrabdistro(base.x+ 31, base.y + base.height, NO_BOUNCE);
500 World::current()->trygrabdistro(base.x, base.y + base.height / 2, NO_BOUNCE);
501 World::current()->trygrabdistro(base.x+ 31, base.y + base.height / 2, NO_BOUNCE);
506 /* Enough distros for a One-up? */
507 if (distros >= DISTROS_LIFEUP)
509 distros = distros - DISTROS_LIFEUP;
510 if(lives < MAX_LIVES)
512 /*We want to hear the sound even, if MAX_LIVES is reached*/
513 play_sound(sounds[SND_LIFEUP], SOUND_CENTER_SPEAKER);
520 if (!safe_timer.started() || (global_frame_counter % 2) == 0)
524 if (invincible_timer.started())
530 cape_right[global_frame_counter % 2]->draw(base.x- scroll_x, base.y);
534 cape_left[global_frame_counter % 2]->draw(
535 base.x- scroll_x, base.y);
542 if (physic.get_velocity_y() != 0)
545 smalltux_jump_right->draw( base.x - scroll_x, base.y - 10);
547 smalltux_jump_left->draw( base.x - scroll_x, base.y - 10);
551 if (fabsf(physic.get_velocity_x()) < 1.0f) // standing
554 smalltux_stand_right->draw( base.x - scroll_x, base.y - 9);
556 smalltux_stand_left->draw( base.x - scroll_x, base.y - 9);
561 tux_right[(global_frame_counter/2) % tux_right.size()]->draw(
562 base.x - scroll_x, base.y - 9);
564 tux_left[(global_frame_counter/2) % tux_left.size()]->draw(
565 base.x - scroll_x, base.y - 9);
571 /* Tux got coffee! */
575 firetux_right[frame_]->draw( base.x- scroll_x, base.y);
579 firetux_left[frame_]->draw( base.x- scroll_x, base.y);
585 if (invincible_timer.started())
587 float capex = base.x + (base.width - bigcape_right[0]->w) / 2;
589 float capey = base.y + (base.height - bigcape_right[0]->h) / 2;
591 /* Draw cape (just not in ducked mode since that looks silly): */
594 bigcape_right[global_frame_counter % 2]->draw(
599 bigcape_left[global_frame_counter % 2]->draw(
608 if (!skidding_timer.started())
610 if (!jumping || physic.get_velocity_y() > 0)
614 bigtux_right[frame_]->draw(
615 base.x- scroll_x - 8, base.y);
619 bigtux_left[frame_]->draw(
620 base.x- scroll_x - 8, base.y);
627 bigtux_right_jump->draw(
628 base.x- scroll_x - 8, base.y);
632 bigtux_left_jump->draw(
633 base.x- scroll_x - 8, base.y);
642 base.x- scroll_x - 8, base.y);
647 base.x- scroll_x - 8, base.y);
655 ducktux_right->draw( base.x- scroll_x - 8, base.y - 16);
659 ducktux_left->draw( base.x- scroll_x - 8, base.y - 16);
665 /* Tux has coffee! */
668 if (!skidding_timer.started())
670 if (!jumping || physic.get_velocity_y() > 0)
674 bigfiretux_right[frame_]->draw(
675 base.x- scroll_x - 8, base.y);
679 bigfiretux_left[frame_]->draw(
680 base.x- scroll_x - 8, base.y);
687 bigfiretux_right_jump->draw(
688 base.x- scroll_x - 8, base.y);
692 bigfiretux_left_jump->draw(
693 base.x- scroll_x - 8, base.y);
701 skidfiretux_right->draw(
702 base.x- scroll_x - 8, base.y);
706 skidfiretux_left->draw(
707 base.x- scroll_x - 8, base.y);
715 duckfiretux_right->draw( base.x- scroll_x - 8, base.y - 16);
719 duckfiretux_left->draw( base.x- scroll_x - 8, base.y - 16);
727 gold_text->drawf("Penguins can fly !:",0,0,A_HMIDDLE,A_VMIDDLE,1);
730 fillrect(base.x - scroll_x, base.y, 32, 32, 75,75,75, 150);
734 Player::collision(void* p_c_object, int c_object)
736 BadGuy* pbad_c = NULL;
741 pbad_c = (BadGuy*) p_c_object;
742 /* Hurt the player if he just touched it: */
744 if (!pbad_c->dying && !dying &&
745 !safe_timer.started() &&
746 pbad_c->mode != HELD)
748 if (pbad_c->mode == FLAT && input.fire == DOWN)
753 else if (pbad_c->mode == KICK)
755 if (base.y < pbad_c->base.y - 16)
757 /* Step on (stop being kicked) */
760 play_sound(sounds[SND_STOMP], SOUND_CENTER_SPEAKER);
764 /* Hurt if you get hit by kicked laptop: */
765 if (!invincible_timer.started())
771 pbad_c->dying = DYING_FALLING;
772 play_sound(sounds[SND_FALL], SOUND_CENTER_SPEAKER);
773 World::current()->add_score(pbad_c->base.x - scroll_x,
775 25 * player_status.score_multiplier);
781 if (!invincible_timer.started())
790 player_status.score_multiplier++;
802 Player::kill(int mode)
804 play_sound(sounds[SND_HURT], SOUND_CENTER_SPEAKER);
806 physic.set_velocity(0, physic.get_velocity_y());
808 if (mode == SHRINK && size == BIG)
817 safe_timer.start(TUX_SAFE_TIME);
821 physic.enable_gravity(true);
822 physic.set_acceleration(0, 0);
823 physic.set_velocity(0, 7);
824 dying = DYING_SQUISHED;
838 bool Player::is_dead()
840 if(base.y > screen->h)
846 /* Remove Tux's power ups */
848 Player::remove_powerups()
856 Player::keep_in_bounds()
858 Level* plevel = World::current()->get_level();
860 /* Keep tux in bounds: */
862 { // Lock Tux to the size of the level, so that he doesn't fall of
866 else if (base.x < scroll_x)
871 /* Keep in-bounds, vertically: */
872 if (base.y > screen->h)
877 int scroll_threshold = screen->w/2 - 80;
880 scroll_x += screen->w/2;
881 // Backscrolling for debug mode
882 if (scroll_x < base.x - 80)
883 scroll_x = base.x - 80;
884 else if (scroll_x > base.x + 80)
885 scroll_x = base.x + 80;
886 scroll_x -= screen->w/2;
893 if (base.x > scroll_threshold + scroll_x
894 && scroll_x < ((World::current()->get_level()->width * 32) - screen->w))
896 // FIXME: Scrolling needs to be handled by a seperate View
897 // class, doing it as a player huck is ugly
899 // Scroll the screen in past center:
900 scroll_x = base.x - scroll_threshold;
902 // Lock the scrolling to the levelsize, so that we don't
903 // scroll over the right border
904 if (scroll_x > 32 * plevel->width - screen->w)
905 scroll_x = 32 * plevel->width - screen->w;