3 // SuperTux - A Jump'n Run
4 // Copyright (C) 2003 Tobias Glaesser <tobi.web@gmx.de>
6 // This program is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU General Public License
8 // as published by the Free Software Foundation; either version 2
9 // of the License, or (at your option) any later version.
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
34 #include "screen/screen.h"
36 // behavior definitions:
37 #define TILES_FOR_BUTTJUMP 3
38 // animation times (in ms):
39 #define SHOOTING_TIME 320
41 #define AUTOSCROLL_DEAD_INTERVAL 300
45 Sprite* smalltux_gameover;
46 Sprite* smalltux_star;
47 Sprite* largetux_star;
48 Sprite* growingtux_left;
49 Sprite* growingtux_right;
51 PlayerSprite smalltux;
52 PlayerSprite largetux;
58 PlayerKeymap::PlayerKeymap()
60 keymap.jump = SDLK_UP;
61 keymap.duck = SDLK_DOWN;
62 keymap.left = SDLK_LEFT;
63 keymap.right = SDLK_RIGHT;
64 keymap.fire = SDLK_LCTRL;
67 void player_input_init(player_input_type* pplayer_input)
69 pplayer_input->down = UP;
70 pplayer_input->fire = UP;
71 pplayer_input->left = UP;
72 pplayer_input->old_fire = UP;
73 pplayer_input->right = UP;
74 pplayer_input->up = UP;
75 pplayer_input->old_up = UP;
90 holding_something = false;
96 got_power = NONE_POWER;
100 previous_base = old_base = base;
108 fall_mode = ON_GROUND;
116 player_input_init(&input);
118 invincible_timer.init(true);
119 skidding_timer.init(true);
120 safe_timer.init(true);
121 frame_timer.init(true);
122 kick_timer.init(true);
123 shooting_timer.init(true);
124 growing_timer.init(true);
130 Player::key_event(SDLKey key, int state)
132 if(key == keymap.right)
137 else if(key == keymap.left)
142 else if(key == keymap.jump)
147 else if(key == keymap.duck)
152 else if(key == keymap.fire)
164 Player::level_begin()
168 previous_base = old_base = base;
173 player_input_init(&input);
175 invincible_timer.init(true);
176 skidding_timer.init(true);
177 safe_timer.init(true);
178 frame_timer.init(true);
179 growing_timer.init(true);
185 Player::action(float elapsed_time)
187 bool jumped_in_solid = false;
189 if(dying && !dying_timer.check()) {
194 if (input.fire == UP)
195 holding_something = false;
198 previous_base = base;
200 /* --- HANDLE TUX! --- */
201 if(dying == DYING_NOT)
204 physic.apply(elapsed_time, base.x, base.y);
206 if(dying == DYING_NOT)
208 base_type target = base;
210 collision_swept_object_map(&old_base, &base);
212 if ((!invincible_timer.started() && !safe_timer.started())
213 && (isspike(base.x, base.y) || isspike(base.x + base.width, base.y)
214 || isspike(base.x, base.y + base.height)
215 || isspike(base.x + base.width, base.y + base.height)))
220 // Don't accelerate Tux if he is running against a wall
221 if (target.x != base.x)
223 physic.set_velocity_x(0);
226 // special exception for cases where we're stuck under tiles after
227 // being ducked. In this case we drift out
228 if(!duck && on_ground() && old_base.x == base.x && old_base.y == base.y
229 && collision_object_map(base))
231 base.x += elapsed_time * WALK_SPEED * (dir ? 1: -1);
232 previous_base = old_base = base;
238 physic.enable_gravity(true);
242 physic.set_velocity_y(0);
243 jumped_in_solid = true;
250 if (physic.get_velocity_y() < 0)
252 base.y = (int)(((int)base.y / 32) * 32);
253 physic.set_velocity_y(0);
256 physic.enable_gravity(false);
257 /* Reset score multiplier (for multi-hits): */
258 if (!invincible_timer.started())
259 player_status.score_multiplier = 1;
264 if (isbrick(base.x, base.y) ||
265 isfullbox(base.x, base.y))
267 Sector::current()->trygrabdistro(
268 Vector(base.x, base.y - 32), BOUNCE);
269 Sector::current()->trybumpbadguy(Vector(base.x, base.y - 64));
271 Sector::current()->trybreakbrick(
272 Vector(base.x, base.y), size == SMALL);
274 bumpbrick(base.x, base.y);
275 Sector::current()->tryemptybox(Vector(base.x, base.y), RIGHT);
278 if (isbrick(base.x+ 31, base.y) ||
279 isfullbox(base.x+ 31, base.y))
281 Sector::current()->trygrabdistro(
282 Vector(base.x+ 31, base.y - 32), BOUNCE);
283 Sector::current()->trybumpbadguy(Vector(base.x+ 31, base.y - 64));
286 Sector::current()->trybreakbrick(
287 Vector(base.x+ 31, base.y), size == SMALL);
289 bumpbrick(base.x+ 31, base.y);
290 Sector::current()->tryemptybox(Vector(base.x+ 31, base.y), LEFT);
302 /* Make sure jumping is off. */
308 /* ---- DONE HANDLING TUX! --- */
311 skidding_timer.check();
312 invincible_timer.check();
320 return ( issolid(base.x + base.width / 2, base.y + base.height) ||
321 issolid(base.x + 1, base.y + base.height) ||
322 issolid(base.x + base.width - 1, base.y + base.height));
326 Player::under_solid()
328 return ( issolid(base.x + base.width / 2, base.y) ||
329 issolid(base.x + 1, base.y) ||
330 issolid(base.x + base.width - 1, base.y) );
334 Player::tiles_on_air(int tiles)
336 for(int t = 0; t != tiles; t++)
338 if(issolid(base.x + base.width / 2, base.y + base.height + (tiles*32)) ||
339 issolid(base.x + 1, base.y + base.height + (tiles*32)) ||
340 issolid(base.x + base.width - 1, base.y + base.height + (tiles*32)))
347 Player::handle_horizontal_input()
349 float vx = physic.get_velocity_x();
350 float vy = physic.get_velocity_y();
351 float ax = physic.get_acceleration_x();
352 float ay = physic.get_acceleration_y();
355 if(input.left == DOWN && input.right == UP && (!duck || physic.get_velocity_y() != 0)) {
359 } else if(input.left == UP && input.right == DOWN && (!duck || physic.get_velocity_y() != 0)) {
365 if (input.fire == UP) {
366 ax = dirsign * WALK_ACCELERATION_X;
368 if(vx >= MAX_WALK_XM && dirsign > 0) {
371 } else if(vx <= -MAX_WALK_XM && dirsign < 0) {
376 ax = dirsign * RUN_ACCELERATION_X;
378 if(vx >= MAX_RUN_XM && dirsign > 0) {
381 } else if(vx <= -MAX_RUN_XM && dirsign < 0) {
387 // we can reach WALK_SPEED without any acceleration
388 if(dirsign != 0 && fabs(vx) < WALK_SPEED) {
389 vx = dirsign * WALK_SPEED;
392 // changing directions?
393 if(on_ground() && ((vx < 0 && dirsign >0) || (vx>0 && dirsign<0))) {
394 if(fabs(vx)>SKID_XM && !skidding_timer.check()) {
395 skidding_timer.start(SKID_TIME);
396 play_sound(sounds[SND_SKID], SOUND_CENTER_SPEAKER);
403 // we get slower when not pressing any keys
405 if(fabs(vx) < WALK_SPEED) {
409 ax = WALK_ACCELERATION_X * 1.5;
411 ax = WALK_ACCELERATION_X * -1.5;
415 // if we're on ice slow down acceleration or deceleration
416 if (isice(base.x, base.y + base.height))
418 /* the acceleration/deceleration rate on ice is inversely proportional to
419 * the current velocity.
422 // increasing 1 will increase acceleration/deceleration rate
423 // decreasing 1 will decrease acceleration/deceleration rate
424 // must stay above zero, though
425 if (ax != 0) ax *= 1 / fabs(vx);
428 physic.set_velocity(vx, vy);
429 physic.set_acceleration(ax, ay);
433 Player::handle_vertical_input()
437 fall_mode = ON_GROUND;
438 last_ground_y = base.y;
440 if(base.y > last_ground_y)
442 else if(fall_mode == ON_GROUND)
447 if(input.up == DOWN && can_jump && on_ground())
449 if(duck) { // only jump a little bit when in duck mode {
450 physic.set_velocity_y(3);
452 // jump higher if we are running
453 if (fabs(physic.get_velocity_x()) > MAX_WALK_XM)
454 physic.set_velocity_y(5.8);
456 physic.set_velocity_y(5.2);
463 play_sound(sounds[SND_JUMP], SOUND_CENTER_SPEAKER);
465 play_sound(sounds[SND_BIGJUMP], SOUND_CENTER_SPEAKER);
467 // Let go of jump key
468 else if(input.up == UP && jumping && physic.get_velocity_y() > 0)
471 physic.set_velocity_y(0);
474 /* In case the player has pressed Down while in a certain range of air,
475 enable butt jump action */
476 if (input.down == DOWN && !butt_jump)
477 if(tiles_on_air(TILES_FOR_BUTTJUMP) && jumping)
480 /* When Down is not held anymore, disable butt jump */
481 if(butt_jump && input.down == UP)
485 if (butt_jump && on_ground() && size == BIG)
489 // Break bricks beneath Tux
490 if(Sector::current()->trybreakbrick(
491 Vector(base.x + 1, base.y + base.height), false)
492 || Sector::current()->trybreakbrick(
493 Vector(base.x + base.width - 1, base.y + base.height), false))
495 physic.set_velocity_y(2);
499 // Kill nearby badguys
500 std::vector<GameObject*> gameobjects = Sector::current()->gameobjects;
501 for (std::vector<GameObject*>::iterator i = gameobjects.begin();
502 i != gameobjects.end();
505 BadGuy* badguy = dynamic_cast<BadGuy*> (*i);
508 if (fabsf(base.x - badguy->base.x) < 300 &&
509 fabsf(base.y - badguy->base.y) < 300 &&
510 (issolid(badguy->base.x + 1, badguy->base.y + badguy->base.height) ||
511 issolid(badguy->base.x + badguy->base.width - 1, badguy->base.y + badguy->base.height)))
517 if ( (issolid(base.x + base.width / 2, base.y + base.height + 64) ||
518 issolid(base.x + 1, base.y + base.height + 64) ||
519 issolid(base.x + base.width - 1, base.y + base.height + 64))
523 && input.old_up == UP)
528 if(on_ground()) /* Make sure jumping is off. */
531 input.old_up = input.up;
535 Player::handle_input()
537 /* Handle horizontal movement: */
538 handle_horizontal_input();
542 if (on_ground() && input.up == UP)
544 handle_vertical_input();
547 if (input.fire == DOWN && input.old_fire == UP && got_power != NONE_POWER)
549 if(Sector::current()->add_bullet(Vector(base.x, base.y + (base.height/2)),
550 physic.get_velocity_x(), dir))
551 shooting_timer.start(SHOOTING_TIME);
552 input.old_fire = DOWN;
555 /* tux animations: */
556 if(!frame_timer.check())
558 frame_timer.start(25);
559 if (input.right == UP && input.left == UP)
566 if ((input.fire == DOWN && (global_frame_counter % 2) == 0) ||
567 (global_frame_counter % 4) == 0)
568 frame_main = (frame_main + 1) % 4;
578 if (input.down == DOWN && size == BIG && !duck && physic.get_velocity_y() == 0 && on_ground())
583 // changing base size confuses collision otherwise
584 old_base = previous_base = base;
586 else if(input.down == UP && size == BIG && duck)
588 // try if we can really unduck
591 // when unducking in air we need some space to do so
592 if(on_ground() || !collision_object_map(base)) {
594 // changing base size confuses collision otherwise
595 old_base = previous_base = base;
597 // undo the ducking changes
605 Player::grow(bool animate)
615 growing_timer.start((int)((growingtux_left->get_frames() / growingtux_left->get_fps()) * 1000));
617 old_base = previous_base = base;
621 Player::grabdistros()
626 Sector::current()->trygrabdistro(Vector(base.x, base.y), NO_BOUNCE);
627 Sector::current()->trygrabdistro(Vector(base.x+ 31, base.y), NO_BOUNCE);
629 Sector::current()->trygrabdistro(
630 Vector(base.x, base.y + base.height), NO_BOUNCE);
631 Sector::current()->trygrabdistro(
632 Vector(base.x+ 31, base.y + base.height), NO_BOUNCE);
636 Sector::current()->trygrabdistro(
637 Vector(base.x, base.y + base.height / 2), NO_BOUNCE);
638 Sector::current()->trygrabdistro(
639 Vector(base.x+ 31, base.y + base.height / 2), NO_BOUNCE);
644 /* Enough distros for a One-up? */
645 if (player_status.distros >= DISTROS_LIFEUP)
647 player_status.distros = player_status.distros - DISTROS_LIFEUP;
648 if(player_status.lives < MAX_LIVES)
649 ++player_status.lives;
650 /*We want to hear the sound even, if MAX_LIVES is reached*/
651 play_sound(sounds[SND_LIFEUP], SOUND_CENTER_SPEAKER);
656 Player::draw(DrawingContext& context)
658 PlayerSprite* sprite;
662 else if (got_power == FIRE_POWER)
664 else if (got_power == ICE_POWER)
669 int layer = LAYER_OBJECTS - 1;
670 Vector pos = Vector(base.x, base.y);
672 if (!safe_timer.started() || (global_frame_counter % 2) == 0)
674 if (dying == DYING_SQUISHED)
676 smalltux_gameover->draw(context, pos, LAYER_OBJECTS);
680 if(growing_timer.check())
683 growingtux_right->draw(context, pos, layer);
685 growingtux_left->draw(context, pos, layer);
687 else if (duck && size != SMALL)
690 sprite->duck_right->draw(context, pos, layer);
692 sprite->duck_left->draw(context, pos, layer);
694 else if (skidding_timer.started())
697 sprite->skid_right->draw(context, pos, layer);
699 sprite->skid_left->draw(context, pos, layer);
701 else if (kick_timer.started())
704 sprite->kick_right->draw(context, pos, layer);
706 sprite->kick_left->draw(context, pos, layer);
708 else if (physic.get_velocity_y() != 0)
711 sprite->jump_right->draw(context, pos, layer);
713 sprite->jump_left->draw(context, pos, layer);
717 if (fabsf(physic.get_velocity_x()) < 1.0f) // standing
720 sprite->stand_right->draw(context, pos, layer);
722 sprite->stand_left->draw(context, pos, layer);
727 sprite->walk_right->draw(context, pos, layer);
729 sprite->walk_left->draw(context, pos, layer);
735 // Draw arm overlay graphics when Tux is holding something
736 if ((holding_something && physic.get_velocity_y() == 0) || shooting_timer.check() && !duck)
739 sprite->grab_right->draw(context, pos, LAYER_OBJECTS + 1);
741 sprite->grab_left->draw(context, pos, LAYER_OBJECTS + 1);
744 // Draw blinking star overlay
745 if (invincible_timer.started() &&
746 (invincible_timer.get_left() > TUX_INVINCIBLE_TIME_WARNING || global_frame_counter % 3))
748 if (size == SMALL || duck)
749 smalltux_star->draw(context, pos, LAYER_OBJECTS + 2);
751 largetux_star->draw(context, pos, LAYER_OBJECTS + 2);
756 fillrect(base.x - viewport.get_translation().x,
757 base.y - viewport.get_translation().y,
758 base.width, base.height, 75,75,75, 150);
763 Player::collision(const MovingObject& other, int collision_type)
766 (void) collision_type;
767 // will be implemented later
771 Player::collision(void* p_c_object, int c_object)
773 BadGuy* pbad_c = NULL;
774 Trampoline* ptramp_c = NULL;
775 FlyingPlatform* pplatform_c = NULL;
780 pbad_c = (BadGuy*) p_c_object;
782 /* Hurt player if he touches a badguy */
783 if (!pbad_c->dying && !dying &&
784 !safe_timer.started() &&
785 pbad_c->mode != BadGuy::HELD)
787 if (pbad_c->mode == BadGuy::FLAT && input.fire == DOWN
788 && !holding_something)
790 holding_something = true;
791 pbad_c->mode = BadGuy::HELD;
794 else if (pbad_c->mode == BadGuy::FLAT)
796 // Don't get hurt if we're kicking a flat badguy!
798 else if (pbad_c->mode == BadGuy::KICK)
800 /* Hurt if you get hit by kicked laptop: */
801 if (!invincible_timer.started())
808 else if (pbad_c->frozen_timer.check() && (pbad_c->kind == BAD_MRBOMB
809 || pbad_c->kind == BAD_JUMPY || pbad_c->kind == BAD_FISH
810 || pbad_c->kind == BAD_SPIKY))
814 if (!invincible_timer.started())
823 player_status.score_multiplier++;
828 ptramp_c = (Trampoline*) p_c_object;
830 // Pick up trampoline
831 if (ptramp_c->mode != Trampoline::M_HELD && input.fire == DOWN && !holding_something && on_ground())
833 holding_something = true;
834 ptramp_c->mode = Trampoline::M_HELD;
835 ptramp_c->base.y -= 8;
837 // Set down trampoline
838 else if (ptramp_c->mode == Trampoline::M_HELD && input.fire != DOWN)
840 holding_something = false;
841 ptramp_c->mode = Trampoline::M_NORMAL;
842 ptramp_c->base.y += 8;
843 ptramp_c->physic.set_velocity(physic.get_velocity_x(), physic.get_velocity_y());
846 // ptramp_c->base.x = base.x + base.width+1;
848 // ptramp_c->base.x = base.x - base.width-1;
851 // Don't let tux walk through trampoline
852 else if (ptramp_c->mode != Trampoline::M_HELD && on_ground())
854 if (physic.get_velocity_x() > 0) // RIGHT
856 physic.set_velocity_x(0);
857 base.x = ptramp_c->base.x - base.width;
859 else if (physic.get_velocity_x() < 0) // LEFT
861 physic.set_velocity_x(0);
862 base.x = ptramp_c->base.x + ptramp_c->base.width;
867 case CO_FLYING_PLATFORM:
868 pplatform_c = (FlyingPlatform*) p_c_object;
870 base.y = pplatform_c->base.y - base.height;
871 physic.set_velocity_x(pplatform_c->get_vel_x());
873 physic.enable_gravity(false);
875 fall_mode = ON_GROUND;
887 Player::kill(HurtMode mode)
892 play_sound(sounds[SND_HURT], SOUND_CENTER_SPEAKER);
894 physic.set_velocity_x(0);
896 if (mode == SHRINK && size == BIG)
898 if (got_power != NONE_POWER)
900 got_power = NONE_POWER;
908 safe_timer.start(TUX_SAFE_TIME);
912 physic.enable_gravity(true);
913 physic.set_acceleration(0, 0);
914 physic.set_velocity(0, 7);
915 --player_status.lives;
916 dying = DYING_SQUISHED;
917 dying_timer.start(3000);
921 /* Remove Tux's power ups */
923 Player::remove_powerups()
925 got_power = NONE_POWER;
931 Player::move(const Vector& vector)
935 old_base = previous_base = base;
939 Player::check_bounds(Camera* camera)
941 /* Keep tux in bounds: */
943 { // Lock Tux to the size of the level, so that he doesn't fall of
948 /* Keep in-bounds, vertically: */
949 if (base.y > Sector::current()->solids->get_height() * 32)
956 // can happen if back scrolling is disabled
957 if(base.x < camera->get_translation().x) {
958 base.x = camera->get_translation().x;
961 if(base.x >= camera->get_translation().x + screen->w - base.width) {
962 base.x = camera->get_translation().x + screen->w - base.width;
968 if(collision_object_map(base)) {