Bouncing on enemies and trampolines also gets a hight bonus from airflower
[supertux.git] / src / object / player.cpp
index 8a09076..3af291c 100644 (file)
@@ -40,9 +40,9 @@
 //#define SWIMMING
 
 namespace {
-static const int TILES_FOR_BUTTJUMP = 3;
 static const float BUTTJUMP_MIN_VELOCITY_Y = 400.0f;
 static const float SHOOTING_TIME = .150f;
+static const float GLIDE_TIME_PER_FLOWER = 0.5f;
 
 /** number of idle stages, including standing */
 static const unsigned int IDLE_STAGE_COUNT = 5;
@@ -73,10 +73,14 @@ static const float SKID_TIME = .3f;
 static const float MAX_WALK_XM = 230;
 /** maximum run velocity (pixel/s) */
 static const float MAX_RUN_XM = 320;
+/** bonus run velocity addition (pixel/s) */
+static const float BONUS_RUN_XM = 80;
 /** maximum horizontal climb velocity */
 static const float MAX_CLIMB_XM = 96;
 /** maximum vertical climb velocity */
 static const float MAX_CLIMB_YM = 128;
+/** maximum vertical glide velocity */
+static const float MAX_GLIDE_YM = 128;
 /** instant velocity when tux starts to walk */
 static const float WALK_SPEED = 100;
 
@@ -88,8 +92,6 @@ static const float ICE_ACCELERATION_MULTIPLIER = 0.25f;
 
 /** time of the kick (kicking mriceblock) animation */
 static const float KICK_TIME = .3f;
-/** time of tux cheering (currently unused) */
-static const float CHEER_TIME = 1.0f;
 
 /** if Tux cannot unduck for this long, he will get hurt */
 static const float UNDUCK_HURT_TIME = 0.25f;
@@ -122,12 +124,15 @@ Player::Player(PlayerStatus* _player_status, const std::string& name_) :
   backflip_direction(),
   peekingX(),
   peekingY(),
+  glide_time(),
+  stone(),
   swimming(),
   speedlimit(),
   scripting_controller_old(0),
   jump_early_apex(),
   on_ice(),
   ice_this_frame(),
+  lightsprite(SpriteManager::current()->create("images/objects/lightmap_light/lightmap_light-tiny.sprite")),
   dir(),
   old_dir(),
   last_ground_y(),
@@ -143,6 +148,8 @@ Player::Player(PlayerStatus* _player_status, const std::string& name_) :
   safe_timer(),
   kick_timer(),
   shooting_timer(),
+  ability_timer(),
+  cooldown_timer(),
   dying_timer(),
   growing(),
   backflip_timer(),
@@ -160,23 +167,23 @@ Player::Player(PlayerStatus* _player_status, const std::string& name_) :
   climbing(0)
 {
   this->name = name_;
-  controller = g_input_manager->get_controller();
+  controller = InputManager::current()->get_controller();
   scripting_controller.reset(new CodeController());
   // if/when we have complete penny gfx, we can
   // load those instead of Tux's sprite in the
   // constructor
-  sprite = sprite_manager->create("images/creatures/tux/tux.sprite");
+  sprite = SpriteManager::current()->create("images/creatures/tux/tux.sprite");
   airarrow = Surface::create("images/engine/hud/airarrow.png");
   idle_timer.start(IDLE_TIME[0]/1000.0f);
 
-  sound_manager->preload("sounds/bigjump.wav");
-  sound_manager->preload("sounds/jump.wav");
-  sound_manager->preload("sounds/hurt.wav");
-  sound_manager->preload("sounds/kill.wav");
-  sound_manager->preload("sounds/skid.wav");
-  sound_manager->preload("sounds/flip.wav");
-  sound_manager->preload("sounds/invincible_start.ogg");
-  sound_manager->preload("sounds/splash.ogg");
+  SoundManager::current()->preload("sounds/bigjump.wav");
+  SoundManager::current()->preload("sounds/jump.wav");
+  SoundManager::current()->preload("sounds/hurt.wav");
+  SoundManager::current()->preload("sounds/kill.wav");
+  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");
 
   init();
 }
@@ -216,10 +223,13 @@ Player::init()
   backflip_direction = 0;
   sprite->set_angle(0.0f);
   visible = true;
+  glide_time = 0;
+  stone = false;
   swimming = false;
   on_ice = false;
   ice_this_frame = false;
   speedlimit = 0; //no special limit
+  lightsprite->set_blend(Blend(GL_SRC_ALPHA, GL_ONE));
 
   on_ground_flag = false;
   grabbed_object = NULL;
@@ -346,11 +356,11 @@ Player::update(float elapsed_time)
   if(!dying && !deactivated)
     handle_input();
 
-/*
+  /*
   // handle_input() calls apply_friction() when Tux is not walking, so we'll have to do this ourselves
   if (deactivated)
-    apply_friction();
-*/
+  apply_friction();
+  */
 
   // extend/shrink tux collision rectangle so that we fall through/walk over 1
   // tile holes
@@ -401,6 +411,8 @@ Player::update(float elapsed_time)
       if (deactivated)
         do_standup();
     }
+    if (player_status->bonus == AIR_BONUS)
+      glide_time = player_status->max_air_time * GLIDE_TIME_PER_FLOWER;
   }
 
   // calculate movement for this frame
@@ -430,13 +442,15 @@ Player::update(float elapsed_time)
       Vector ppos = Vector(px, py);
       Vector pspeed = Vector(0, 0);
       Vector paccel = Vector(0, 0);
-      Sector::current()->add_object(new SpriteParticle("images/objects/particles/sparkle.sprite",
-                                                       // draw bright sparkle when there is lots of time left, dark sparkle when invincibility is about to end
-                                                       (invincible_timer.get_timeleft() > TUX_INVINCIBLE_TIME_WARNING) ?
-                                                       // make every other a longer sparkle to make trail a bit fuzzy
-                                                       (size_t(game_time*20)%2) ? "small" : "medium"
-                                                       :
-                                                       "dark", ppos, ANCHOR_MIDDLE, pspeed, paccel, LAYER_OBJECTS+1+5));
+      Sector::current()->add_object(std::make_shared<SpriteParticle>(
+                                      "images/objects/particles/sparkle.sprite",
+                                      // draw bright sparkle when there is lots of time left,
+                                      // dark sparkle when invincibility is about to end
+                                      (invincible_timer.get_timeleft() > TUX_INVINCIBLE_TIME_WARNING) ?
+                                      // make every other a longer sparkle to make trail a bit fuzzy
+                                      (size_t(game_time*20)%2) ? "small" : "medium"
+                                      :
+                                      "dark", ppos, ANCHOR_MIDDLE, pspeed, paccel, LAYER_OBJECTS+1+5));
     }
   }
 
@@ -525,11 +539,11 @@ Player::handle_horizontal_input()
       ax = dirsign * RUN_ACCELERATION_X;
     }
     // limit speed
-    if(vx >= MAX_RUN_XM && dirsign > 0) {
-      vx = MAX_RUN_XM;
+    if(vx >= MAX_RUN_XM + BONUS_RUN_XM *((player_status->bonus == AIR_BONUS) ? 1 : 0) && dirsign > 0) {
+      vx = MAX_RUN_XM + BONUS_RUN_XM *((player_status->bonus == AIR_BONUS) ? 1 : 0);
       ax = 0;
-    } else if(vx <= -MAX_RUN_XM && dirsign < 0) {
-      vx = -MAX_RUN_XM;
+    } else if(vx <= -MAX_RUN_XM - BONUS_RUN_XM *((player_status->bonus == AIR_BONUS) ? 1 : 0) && dirsign < 0) {
+      vx = -MAX_RUN_XM - BONUS_RUN_XM *((player_status->bonus == AIR_BONUS) ? 1 : 0);
       ax = 0;
     }
   }
@@ -550,10 +564,10 @@ Player::handle_horizontal_input()
     // let's skid!
     if(fabs(vx)>SKID_XM && !skidding_timer.started()) {
       skidding_timer.start(SKID_TIME);
-      sound_manager->play("sounds/skid.wav");
+      SoundManager::current()->play("sounds/skid.wav");
       // dust some particles
       Sector::current()->add_object(
-        new Particles(
+        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,
@@ -643,8 +657,8 @@ Player::do_backflip() {
 
   backflip_direction = (dir == LEFT)?(+1):(-1);
   backflipping = true;
-  do_jump(-580);
-  sound_manager->play("sounds/flip.wav");
+  do_jump((player_status->bonus == AIR_BONUS) ? -720 : -580);
+  SoundManager::current()->play("sounds/flip.wav");
   backflip_timer.start(TUX_BACKFLIP_TIME);
 }
 
@@ -661,9 +675,9 @@ Player::do_jump(float yspeed) {
 
   // play sound
   if (is_big()) {
-    sound_manager->play("sounds/bigjump.wav");
+    SoundManager::current()->play("sounds/bigjump.wav");
   } else {
-    sound_manager->play("sounds/jump.wav");
+    SoundManager::current()->play("sounds/jump.wav");
   }
 }
 
@@ -707,16 +721,40 @@ Player::handle_vertical_input()
         do_backflip();
       }
     } else {
+      // airflower allows for higher jumps-
       // jump a bit higher if we are running; else do a normal jump
-      if (fabs(physic.get_velocity_x()) > MAX_WALK_XM) do_jump(-580); else do_jump(-520);
+      if(player_status->bonus == AIR_BONUS)
+        do_jump((fabs(physic.get_velocity_x()) > MAX_WALK_XM) ? -620 : -580);
+      else
+        do_jump((fabs(physic.get_velocity_x()) > MAX_WALK_XM) ? -580 : -520);
     }
-  }
+    // 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);
+      else if (ability_timer.started()) {
+        log_debug << ability_timer.get_timeleft() << std::endl;
+        if (ability_timer.get_timeleft() <= 0.05f) {
+          glide_time = 0;
+          ability_timer.stop();
+        } else {
+          physic.set_velocity_y(MAX_GLIDE_YM);
+          physic.set_acceleration_y(0);
+        }
+      }
+    }
+      /*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) {
       jumping = false;
       early_jump_apex();
     }
+    if (player_status->bonus == AIR_BONUS && ability_timer.started()){
+      glide_time = ability_timer.get_timeleft();
+      ability_timer.stop();
+    }
   }
 
   if(jump_early_apex && physic.get_velocity_y() >= 0) {
@@ -953,6 +991,10 @@ Player::add_bonus(const std::string& bonustype)
     type = FIRE_BONUS;
   } else if(bonustype == "iceflower") {
     type = ICE_BONUS;
+  } else if(bonustype == "airflower") {
+    type = AIR_BONUS;
+  } else if(bonustype == "earthflower") {
+    type = EARTH_BONUS;
   } else if(bonustype == "none") {
     type = NO_BONUS;
   } else {
@@ -974,11 +1016,7 @@ Player::add_bonus(BonusType type, bool animate)
 
   // ignore GROWUP_BONUS if we're already big
   if (type == GROWUP_BONUS) {
-    if (player_status->bonus == GROWUP_BONUS)
-      return true;
-    if (player_status->bonus == FIRE_BONUS)
-      return true;
-    if (player_status->bonus == ICE_BONUS)
+    if (!player_status->bonus == NO_BONUS)
       return true;
   }
 
@@ -1011,7 +1049,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(new 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/firetux-helmet.sprite", action, ppos, ANCHOR_TOP, pspeed, paccel, LAYER_OBJECTS-1));
       if (climbing) stop_climbing(*climbing);
     }
     if ((player_status->bonus == ICE_BONUS) && (animate)) {
@@ -1020,14 +1058,36 @@ 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(new 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/icetux-cap.sprite", action, ppos, ANCHOR_TOP, pspeed, paccel, LAYER_OBJECTS-1));
+      if (climbing) stop_climbing(*climbing);
+    }
+    if ((player_status->bonus == AIR_BONUS) && (animate)) {
+      // visually lose hat
+      Vector ppos = Vector((bbox.p1.x + bbox.p2.x) / 2, bbox.p1.y);
+      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));
+      if (climbing) stop_climbing(*climbing);
+    }
+    if ((player_status->bonus == EARTH_BONUS) && (animate)) {
+      // visually lose hard-hat
+      Vector ppos = Vector((bbox.p1.x + bbox.p2.x) / 2, bbox.p1.y);
+      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));
       if (climbing) stop_climbing(*climbing);
     }
     player_status->max_fire_bullets = 0;
     player_status->max_ice_bullets = 0;
+    player_status->max_air_time = 0;
+    player_status->max_earth_time = 0;
   }
   if (type == FIRE_BONUS) player_status->max_fire_bullets++;
   if (type == ICE_BONUS) player_status->max_ice_bullets++;
+  if (type == AIR_BONUS) player_status->max_air_time++;
+  if (type == EARTH_BONUS) player_status->max_earth_time++;
 
   player_status->bonus = type;
   return true;
@@ -1078,6 +1138,10 @@ Player::draw(DrawingContext& context)
     sa_prefix = "fire";
   else if (player_status->bonus == ICE_BONUS)
     sa_prefix = "ice";
+  else if (player_status->bonus == AIR_BONUS)
+    sa_prefix = "ice";
+  else if (player_status->bonus == EARTH_BONUS)
+    sa_prefix = "fire";
   else
     sa_prefix = "small";
 
@@ -1164,6 +1228,13 @@ Player::draw(DrawingContext& context)
     ;  // 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();
+    }
   }
 
 }
@@ -1185,7 +1256,7 @@ Player::collision_tile(uint32_t tile_attributes)
     if( tile_attributes & Tile::WATER ){
       swimming = true;
       no_water = false;
-      sound_manager->play( "sounds/splash.ogg" );
+      SoundManager::current()->play( "sounds/splash.ogg" );
     }
   }
 #endif
@@ -1211,12 +1282,12 @@ Player::collision_solid(const CollisionHit& hit)
       does_buttjump = false;
       physic.set_velocity_y(-300);
       on_ground_flag = false;
-      Sector::current()->add_object(new Particles(
+      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));
-      Sector::current()->add_object(new Particles(
+      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,
@@ -1285,7 +1356,7 @@ Player::collision(GameObject& other, const CollisionHit& hit)
 void
 Player::make_invincible()
 {
-  sound_manager->play("sounds/invincible_start.ogg");
+  SoundManager::current()->play("sounds/invincible_start.ogg");
   invincible_timer.start(TUX_INVINCIBLE_TIME);
   Sector::current()->play_music(HERRING_MUSIC);
 }
@@ -1306,11 +1377,15 @@ Player::kill(bool completely)
 
   physic.set_velocity_x(0);
 
+  sprite->set_angle(0.0f);
+
   if(!completely && is_big()) {
-    sound_manager->play("sounds/hurt.wav");
+    SoundManager::current()->play("sounds/hurt.wav");
 
     if(player_status->bonus == FIRE_BONUS
-       || player_status->bonus == ICE_BONUS) {
+      || player_status->bonus == ICE_BONUS
+      || player_status->bonus == AIR_BONUS
+      || player_status->bonus == EARTH_BONUS) {
       safe_timer.start(TUX_SAFE_TIME);
       set_bonus(GROWUP_BONUS, true);
     } else if(player_status->bonus == GROWUP_BONUS) {
@@ -1326,7 +1401,7 @@ Player::kill(bool completely)
       duck = false;
     }
   } else {
-    sound_manager->play("sounds/kill.wav");
+    SoundManager::current()->play("sounds/kill.wav");
 
     // do not die when in edit mode
     if (edit_mode) {
@@ -1339,7 +1414,7 @@ Player::kill(bool completely)
       for (int i = 0; i < 5; i++)
       {
         // the numbers: starting x, starting y, velocity y
-        Sector::current()->add_object(new FallingCoin(get_pos() +
+        Sector::current()->add_object(std::make_shared<FallingCoin>(get_pos() +
                                                       Vector(graphicsRandom.rand(5), graphicsRandom.rand(-32,18)),
                                                       graphicsRandom.rand(-100,100)));
       }
@@ -1362,7 +1437,7 @@ Player::kill(bool completely)
 
     // TODO: need nice way to handle players dying in co-op mode
     Sector::current()->effect->fade_out(3.0);
-    sound_manager->stop_music(3.0);
+    SoundManager::current()->stop_music(3.0);
   }
 }
 
@@ -1436,10 +1511,12 @@ Player::get_velocity()
 void
 Player::bounce(BadGuy& )
 {
-  if(controller->hold(Controller::JUMP))
-    physic.set_velocity_y(-520);
-  else
-    physic.set_velocity_y(-300);
+  if(!(player_status->bonus == AIR_BONUS))
+    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;
+  }
 }
 
 //scripting Functions Below