Fade out and pause music on death and resume on restart of level, fixes #1064
[supertux.git] / src / object / player.cpp
index 376cdcf..93c1094 100644 (file)
@@ -43,6 +43,7 @@ namespace {
 static const float BUTTJUMP_MIN_VELOCITY_Y = 400.0f;
 static const float SHOOTING_TIME = .150f;
 static const float GLIDE_TIME_PER_FLOWER = 0.5f;
+static const float STONE_TIME_PER_FLOWER = 2.0f;
 
 /** number of idle stages, including standing */
 static const unsigned int IDLE_STAGE_COUNT = 5;
@@ -124,7 +125,7 @@ Player::Player(PlayerStatus* _player_status, const std::string& name_) :
   backflip_direction(),
   peekingX(),
   peekingY(),
-  glide_time(),
+  ability_time(),
   stone(),
   swimming(),
   speedlimit(),
@@ -132,7 +133,8 @@ Player::Player(PlayerStatus* _player_status, const std::string& name_) :
   jump_early_apex(),
   on_ice(),
   ice_this_frame(),
-  lightsprite(SpriteManager::current()->create("images/objects/lightmap_light/lightmap_light-tiny.sprite")),
+  lightsprite(SpriteManager::current()->create("images/creatures/tux/light.sprite")),
+  powersprite(SpriteManager::current()->create("images/creatures/tux/powerups.sprite")),
   dir(),
   old_dir(),
   last_ground_y(),
@@ -183,7 +185,7 @@ Player::Player(PlayerStatus* _player_status, const std::string& name_) :
   SoundManager::current()->preload("sounds/skid.wav");
   SoundManager::current()->preload("sounds/flip.wav");
   SoundManager::current()->preload("sounds/invincible_start.ogg");
-  SoundManager::current()->preload("sounds/splash.ogg");
+  SoundManager::current()->preload("sounds/splash.wav");
 
   init();
 }
@@ -222,8 +224,10 @@ Player::init()
   backflipping = false;
   backflip_direction = 0;
   sprite->set_angle(0.0f);
+  powersprite->set_angle(0.0f);
+  lightsprite->set_angle(0.0f);
   visible = true;
-  glide_time = 0;
+  ability_time = 0;
   stone = false;
   swimming = false;
   on_ice = false;
@@ -336,6 +340,8 @@ Player::trigger_sequence(std::string sequence_name)
   backflipping = false;
   backflip_direction = 0;
   sprite->set_angle(0.0f);
+  powersprite->set_angle(0.0f);
+  lightsprite->set_angle(0.0f);
   GameSession::current()->start_sequence(sequence_name);
 }
 
@@ -386,6 +392,11 @@ Player::update(float elapsed_time)
     if (backflip_timer.started()) physic.set_velocity_x(100 * backflip_direction);
     //rotate sprite during flip
     sprite->set_angle(sprite->get_angle() + (dir==LEFT?1:-1) * elapsed_time * (360.0f / 0.5f));
+    if (player_status->bonus == EARTH_BONUS || player_status->bonus == AIR_BONUS) {
+      powersprite->set_angle(sprite->get_angle());
+      if (player_status->bonus == EARTH_BONUS)
+        lightsprite->set_angle(sprite->get_angle());
+    }
   }
 
   // set fall mode...
@@ -405,14 +416,19 @@ Player::update(float elapsed_time)
     if (backflipping && (backflip_timer.get_timegone() > 0.15f)) {
       backflipping = false;
       backflip_direction = 0;
-      sprite->set_angle(0.0f);
+      physic.set_velocity_x(0);
+      if (!stone) {
+        sprite->set_angle(0.0f);
+        powersprite->set_angle(0.0f);
+        lightsprite->set_angle(0.0f);
+      }
 
       // if controls are currently deactivated, we take care of standing up ourselves
       if (deactivated)
         do_standup();
     }
     if (player_status->bonus == AIR_BONUS)
-      glide_time = player_status->max_air_time * GLIDE_TIME_PER_FLOWER;
+      ability_time = player_status->max_air_time * GLIDE_TIME_PER_FLOWER;
   }
 
   // calculate movement for this frame
@@ -568,10 +584,9 @@ Player::handle_horizontal_input()
       // dust some particles
       Sector::current()->add_object(
         std::make_shared<Particles>(
-          Vector(dir == RIGHT ? get_bbox().p2.x : get_bbox().p1.x, get_bbox().p2.y),
-          dir == RIGHT ? 270+20 : 90-40, dir == RIGHT ? 270+40 : 90-20,
-          Vector(280, -260), Vector(0, 300), 3, Color(.4f, .4f, .4f), 3, .8f,
-          LAYER_OBJECTS+1));
+          Vector(dir == LEFT ? get_bbox().p2.x : get_bbox().p1.x, get_bbox().p2.y),
+          dir == LEFT ? 50 : -70, dir == LEFT ? 70 : -50, 260, 280,
+          Vector(0, 300), 3, Color(.4f, .4f, .4f), 3, .8f, LAYER_OBJECTS+1));
 
       ax *= 2.5;
     } else {
@@ -632,6 +647,8 @@ Player::do_standup() {
     return;
   if (backflipping)
     return;
+  if (stone)
+    return;
 
   if (adjust_height(BIG_TUX_HEIGHT)) {
     duck = false;
@@ -730,12 +747,12 @@ Player::handle_vertical_input()
     }
     // airflower glide only when holding jump key
   } else  if (controller->hold(Controller::JUMP) && player_status->bonus == AIR_BONUS && physic.get_velocity_y() > MAX_GLIDE_YM) {
-      if (glide_time > 0 && !ability_timer.started())
-        ability_timer.start(glide_time);
+      if (ability_time > 0 && !ability_timer.started())
+        ability_timer.start(ability_time);
       else if (ability_timer.started()) {
         // glide stops after some duration or if buttjump is initiated
         if ((ability_timer.get_timeleft() <= 0.05f) || controller->hold(Controller::DOWN)) {
-          glide_time = 0;
+          ability_time = 0;
           ability_timer.stop();
         } else {
           physic.set_velocity_y(MAX_GLIDE_YM);
@@ -743,8 +760,8 @@ Player::handle_vertical_input()
         }
       }
     }
-      /*ability_timer.started() ? gliding = true : ability_timer.start(player_status->max_air_time);*/
-  //}
+
+
   // Let go of jump key
   else if(!controller->hold(Controller::JUMP)) {
     if (!backflipping && jumping && physic.get_velocity_y() < 0) {
@@ -752,7 +769,7 @@ Player::handle_vertical_input()
       early_jump_apex();
     }
     if (player_status->bonus == AIR_BONUS && ability_timer.started()){
-      glide_time = ability_timer.get_timeleft();
+      ability_time = ability_timer.get_timeleft();
       ability_timer.stop();
     }
   }
@@ -819,14 +836,14 @@ Player::handle_input()
   }
 
   /* Handle horizontal movement: */
-  if (!backflipping) handle_horizontal_input();
+  if (!backflipping && !stone) handle_horizontal_input();
 
   /* Jump/jumping? */
   if (on_ground())
     can_jump = true;
 
   /* Handle vertical movement: */
-  handle_vertical_input();
+  if (!stone) handle_vertical_input();
 
   /* Shoot! */
   if (controller->pressed(Controller::ACTION) && (player_status->bonus == FIRE_BONUS || player_status->bonus == ICE_BONUS)) {
@@ -844,8 +861,40 @@ Player::handle_input()
     }
   }
 
+  /* Turn to Stone */
+  if (controller->pressed(Controller::DOWN) && player_status->bonus == EARTH_BONUS && !cooldown_timer.started()) {
+    if (controller->hold(Controller::ACTION) && !ability_timer.started()) {
+      ability_timer.start(player_status->max_earth_time * STONE_TIME_PER_FLOWER);
+      powersprite->stop_animation();
+      stone = true;
+      physic.set_gravity_modifier(1.0f); // Undo jump_early_apex
+    }
+  }
+
+  if (stone)
+    apply_friction();
+
+  /* Revert from Stone */
+  if (stone && (!controller->hold(Controller::ACTION) || ability_timer.get_timeleft() <= 0.5f)) {
+    cooldown_timer.start(ability_timer.get_timegone()/2.0f); //The longer stone form is used, the longer until it can be used again
+    ability_timer.stop();
+    sprite->set_angle(0.0f);
+    powersprite->set_angle(0.0f);
+    lightsprite->set_angle(0.0f);
+    stone = false;
+    for (int i = 0; i < 8; i++)
+    {
+      Vector ppos = Vector(get_bbox().get_left() + 8 + 16*((int)i/4), get_bbox().get_top() + 16*(i%4));
+      float grey = graphicsRandom.randf(.4f, .8f);
+      Color pcolor = Color(grey, grey, grey);
+      Sector::current()->add_object(std::make_shared<Particles>(ppos, -60, 240, 42, 81, Vector(0, 500),
+                                                                8, pcolor, 4 + graphicsRandom.randf(-0.4, 0.4),
+                                                                0.8 + graphicsRandom.randf(0, 0.4), LAYER_OBJECTS+2));
+    }
+  }
+
   /* Duck or Standup! */
-  if (controller->hold(Controller::DOWN)) {
+  if (controller->hold(Controller::DOWN) && !stone) {
     do_duck();
   } else {
     do_standup();
@@ -888,6 +937,8 @@ Player::handle_input()
     backflipping = false;
     backflip_direction = 0;
     sprite->set_angle(0.0f);
+    powersprite->set_angle(0.0f);
+    lightsprite->set_angle(0.0f);
   }
 }
 
@@ -931,8 +982,6 @@ Player::try_grab()
       // make sure the Portable is a MovingObject
       MovingObject* moving_object = dynamic_cast<MovingObject*> (portable);
       assert(moving_object);
-      if(moving_object == NULL)
-        continue;
 
       // make sure the Portable isn't currently non-solid
       if(moving_object->get_group() == COLGROUP_DISABLED) continue;
@@ -1022,7 +1071,7 @@ Player::add_bonus(BonusType type, bool animate)
 
   // ignore GROWUP_BONUS if we're already big
   if (type == GROWUP_BONUS) {
-    if (!player_status->bonus == NO_BONUS)
+    if (player_status->bonus != NO_BONUS)
       return true;
   }
 
@@ -1073,7 +1122,7 @@ Player::set_bonus(BonusType type, bool animate)
       Vector pspeed = Vector(((dir==LEFT) ? +100 : -100), -300);
       Vector paccel = Vector(0, 1000);
       std::string action = (dir==LEFT)?"left":"right";
-      Sector::current()->add_object(std::make_shared<SpriteParticle>("images/objects/particles/icetux-cap.sprite", action, ppos, ANCHOR_TOP, pspeed, paccel, LAYER_OBJECTS-1));
+      Sector::current()->add_object(std::make_shared<SpriteParticle>("images/objects/particles/airtux-hat.sprite", action, ppos, ANCHOR_TOP, pspeed, paccel, LAYER_OBJECTS-1));
       if (climbing) stop_climbing(*climbing);
     }
     if ((player_status->bonus == EARTH_BONUS) && (animate)) {
@@ -1082,7 +1131,7 @@ Player::set_bonus(BonusType type, bool animate)
       Vector pspeed = Vector(((dir==LEFT) ? +100 : -100), -300);
       Vector paccel = Vector(0, 1000);
       std::string action = (dir==LEFT)?"left":"right";
-      Sector::current()->add_object(std::make_shared<SpriteParticle>("images/objects/particles/firetux-helmet.sprite", action, ppos, ANCHOR_TOP, pspeed, paccel, LAYER_OBJECTS-1));
+      Sector::current()->add_object(std::make_shared<SpriteParticle>("images/objects/particles/earthtux-hardhat.sprite", action, ppos, ANCHOR_TOP, pspeed, paccel, LAYER_OBJECTS-1));
       if (climbing) stop_climbing(*climbing);
     }
     player_status->max_fire_bullets = 0;
@@ -1145,9 +1194,9 @@ Player::draw(DrawingContext& context)
   else if (player_status->bonus == ICE_BONUS)
     sa_prefix = "ice";
   else if (player_status->bonus == AIR_BONUS)
-    sa_prefix = "ice";
+    sa_prefix = "air";
   else if (player_status->bonus == EARTH_BONUS)
-    sa_prefix = "fire";
+    sa_prefix = "earth";
   else
     sa_prefix = "small";
 
@@ -1166,6 +1215,9 @@ Player::draw(DrawingContext& context)
     // do_duck() will take care of cancelling growing manually
     // update() will take care of cancelling when growing completed
   }
+  else if (stone) {
+    sprite->set_action(sprite->get_action()+"-stone");
+  }
   else if (climbing) {
     sprite->set_action(sa_prefix+"-climbing"+sa_postfix);
   }
@@ -1219,6 +1271,13 @@ Player::draw(DrawingContext& context)
     }
   }
 
+  /* Set Tux powerup sprite action */
+  if (player_status->bonus == EARTH_BONUS) {
+    powersprite->set_action(sprite->get_action());
+    lightsprite->set_action(sprite->get_action());
+  } else if (player_status->bonus == AIR_BONUS)
+    powersprite->set_action(sprite->get_action());
+
   /*
   // Tux is holding something
   if ((grabbed_object != 0 && physic.get_velocity_y() == 0) ||
@@ -1232,16 +1291,36 @@ Player::draw(DrawingContext& context)
   /* Draw Tux */
   if (safe_timer.started() && size_t(game_time*40)%2)
     ;  // don't draw Tux
-  else {
-    sprite->draw(context, get_pos(), LAYER_OBJECTS + 1);
-    // draw light with earthflower bonus
-    if (player_status->bonus == EARTH_BONUS){
-      context.push_target();
-      context.set_target(DrawingContext::LIGHTMAP);
-      lightsprite->draw(context, get_pos() + Vector(dir==LEFT ? 0 : 32, 0), 0);
-      context.pop_target();
+  else if (player_status->bonus == EARTH_BONUS){ // draw special effects with earthflower bonus
+    // shake at end of maximum stone duration
+    Vector shake_delta = (stone && ability_timer.get_timeleft() < 1.0f) ? Vector(graphicsRandom.rand(-3,3), 0) : Vector(0,0);
+    sprite->draw(context, get_pos() + shake_delta, LAYER_OBJECTS + 1);
+    // draw hardhat
+    powersprite->draw(context, get_pos() + shake_delta, LAYER_OBJECTS + 1);
+    // light
+    context.push_target();
+    context.set_target(DrawingContext::LIGHTMAP);
+    lightsprite->draw(context, get_pos(), 0);
+    context.pop_target();
+    // give an indicator that stone form cannot be used for a while
+    if (cooldown_timer.started() && graphicsRandom.rand(0, 4) == 0) {
+      float px = graphicsRandom.randf(bbox.p1.x, bbox.p2.x);
+      float py = bbox.p2.y+8;
+      Vector ppos = Vector(px, py);
+      Sector::current()->add_object(std::make_shared<SpriteParticle>(
+        "images/objects/particles/sparkle.sprite", "dark",
+        ppos, ANCHOR_MIDDLE, Vector(0, 0), Vector(0, 0), LAYER_OBJECTS+1+5));
     }
   }
+  else {
+    if(dying)
+      sprite->draw(context, get_pos(), Sector::current()->get_foremost_layer() + 1);
+    else
+      sprite->draw(context, get_pos(), LAYER_OBJECTS + 1);
+
+    if (player_status->bonus == AIR_BONUS)
+      powersprite->draw(context, get_pos(), LAYER_OBJECTS + 1);
+  }
 
 }
 
@@ -1262,7 +1341,7 @@ Player::collision_tile(uint32_t tile_attributes)
     if( tile_attributes & Tile::WATER ){
       swimming = true;
       no_water = false;
-      SoundManager::current()->play( "sounds/splash.ogg" );
+      SoundManager::current()->play( "sounds/splash.wav" );
     }
   }
 #endif
@@ -1290,14 +1369,12 @@ Player::collision_solid(const CollisionHit& hit)
       on_ground_flag = false;
       Sector::current()->add_object(std::make_shared<Particles>(
                                       Vector(get_bbox().p2.x, get_bbox().p2.y),
-                                      270+20, 270+40,
-                                      Vector(280, -260), Vector(0, 300), 3, Color(.4f, .4f, .4f), 3, .8f,
-                                      LAYER_OBJECTS+1));
+                                      50, 70, 260, 280, Vector(0, 300), 3,
+                                      Color(.4f, .4f, .4f), 3, .8f, LAYER_OBJECTS+1));
       Sector::current()->add_object(std::make_shared<Particles>(
                                       Vector(get_bbox().p1.x, get_bbox().p2.y),
-                                      90-40, 90-20,
-                                      Vector(280, -260), Vector(0, 300), 3, Color(.4f, .4f, .4f), 3, .8f,
-                                      LAYER_OBJECTS+1));
+                                      -70, -50, 260, 280, Vector(0, 300), 3,
+                                      Color(.4f, .4f, .4f), 3, .8f, LAYER_OBJECTS+1));
       Sector::current()->camera->shake(.1f, 0, 5);
     }
 
@@ -1352,6 +1429,8 @@ Player::collision(GameObject& other, const CollisionHit& hit)
   if(badguy != NULL) {
     if(safe_timer.started() || invincible_timer.started())
       return FORCE_MOVE;
+    if(stone)
+      return ABORT_MOVE;
 
     return CONTINUE;
   }
@@ -1374,7 +1453,7 @@ Player::kill(bool completely)
   if(dying || deactivated || is_winning() )
     return;
 
-  if(!completely && (safe_timer.started() || invincible_timer.started()))
+  if(!completely && (safe_timer.started() || invincible_timer.started() || stone))
     return;
 
   growing = false;
@@ -1384,6 +1463,8 @@ Player::kill(bool completely)
   physic.set_velocity_x(0);
 
   sprite->set_angle(0.0f);
+  powersprite->set_angle(0.0f);
+  lightsprite->set_angle(0.0f);
 
   if(!completely && is_big()) {
     SoundManager::current()->play("sounds/hurt.wav");
@@ -1400,6 +1481,8 @@ Player::kill(bool completely)
       duck = false;
       backflipping = false;
       sprite->set_angle(0.0f);
+      powersprite->set_angle(0.0f);
+      lightsprite->set_angle(0.0f);
       set_bonus(NO_BONUS, true);
     } else if(player_status->bonus == NO_BONUS) {
       safe_timer.start(TUX_SAFE_TIME);
@@ -1443,7 +1526,7 @@ Player::kill(bool completely)
 
     // TODO: need nice way to handle players dying in co-op mode
     Sector::current()->effect->fade_out(3.0);
-    SoundManager::current()->stop_music(3.0);
+    SoundManager::current()->pause_music(3.0);
   }
 }
 
@@ -1460,6 +1543,8 @@ Player::move(const Vector& vector)
   duck = false;
   backflipping = false;
   sprite->set_angle(0.0f);
+  powersprite->set_angle(0.0f);
+  lightsprite->set_angle(0.0f);
   last_ground_y = vector.y;
   if (climbing) stop_climbing(*climbing);
 
@@ -1521,7 +1606,7 @@ Player::bounce(BadGuy& )
     physic.set_velocity_y(controller->hold(Controller::JUMP) ? -520 : -300);
   else {
     physic.set_velocity_y(controller->hold(Controller::JUMP) ? -580 : -340);
-    glide_time = player_status->max_air_time * GLIDE_TIME_PER_FLOWER;
+    ability_time = player_status->max_air_time * GLIDE_TIME_PER_FLOWER;
   }
 }
 
@@ -1598,6 +1683,8 @@ Player::start_climbing(Climbable& climbable)
     backflipping = false;
     backflip_direction = 0;
     sprite->set_angle(0.0f);
+    powersprite->set_angle(0.0f);
+    lightsprite->set_angle(0.0f);
   }
 }