text: /**
   * @file
   * @brief Misc function related to player transformations.
  **/
  
  #include "AppHdr.h"
  
  #include "transform.h"
  
  #include <cstdio>
  #include <cstring>
  
  #include "artefact.h"
  #include "art-enum.h"
  #include "cloud.h"
  #include "delay.h"
  #include "english.h"
  #include "env.h"
  #include "god-abil.h"
  #include "god-item.h"
  #include "god-passive.h" // passive_t::resist_polymorph
  #include "invent.h" // check_old_item_warning
  #include "item-use.h"
  #include "item-name.h"
  #include "item-prop.h"
  #include "items.h"
  #include "message.h"
  #include "mon-death.h"
  #include "mutation.h"
  #include "output.h"
  #include "player-equip.h"
  #include "player-stats.h"
  #include "prompt.h"
  #include "religion.h"
  #include "spl-cast.h"
  #include "state.h"
  #include "stringutil.h"
  #include "terrain.h"
  #include "traps.h"
  #include "xom.h"
  
  // transform slot enums into flags
  #define SLOTF(s) (1 << s)
  
  static const int EQF_NONE = 0;
  // "hand" slots (not rings)
  static const int EQF_HANDS = SLOTF(EQ_WEAPON) | SLOTF(EQ_SHIELD)
                               | SLOTF(EQ_GLOVES);
  // core body slots (statue form)
  static const int EQF_STATUE = SLOTF(EQ_GLOVES) | SLOTF(EQ_BOOTS)
                                | SLOTF(EQ_BODY_ARMOUR);
  // more core body slots (Lear's Hauberk)
  static const int EQF_LEAR = EQF_STATUE | SLOTF(EQ_HELMET);
  // everything you can (W)ear
  static const int EQF_WEAR = EQF_LEAR | SLOTF(EQ_CLOAK) | SLOTF(EQ_SHIELD);
  // everything but jewellery
  static const int EQF_PHYSICAL = EQF_HANDS | EQF_WEAR;
  // all rings (except for the macabre finger amulet's)
  static const int EQF_RINGS = SLOTF(EQ_LEFT_RING) | SLOTF(EQ_RIGHT_RING)
                               | SLOTF(EQ_RING_ONE) | SLOTF(EQ_RING_TWO)
                               | SLOTF(EQ_RING_THREE) | SLOTF(EQ_RING_FOUR)
                               | SLOTF(EQ_RING_FIVE) | SLOTF(EQ_RING_SIX)
                               | SLOTF(EQ_RING_SEVEN) | SLOTF(EQ_RING_EIGHT);
  // amulet & pal
  static const int EQF_AMULETS = SLOTF(EQ_AMULET) | SLOTF(EQ_RING_AMULET);
  // everything
  static const int EQF_ALL = EQF_PHYSICAL | EQF_RINGS | EQF_AMULETS;
  
  static const FormAttackVerbs DEFAULT_VERBS = FormAttackVerbs(nullptr, nullptr,
                                                               nullptr, nullptr);
  static const FormAttackVerbs ANIMAL_VERBS = FormAttackVerbs("hit", "bite",
                                                              "maul", "maul");
  
  static const FormDuration DEFAULT_DURATION = FormDuration(20, PS_DOUBLE, 100);
  static const FormDuration BAD_DURATION = FormDuration(15, PS_ONE_AND_A_HALF,
                                                        100);
  
  // Class form_entry and the formdata array
  #include "form-data.h"
  
  static const form_entry &_find_form_entry(transformation form)
  {
      for (const form_entry &entry : formdata)
          if (entry.tran == form)
              return entry;
      die("No formdata entry found for form %d", (int)form);
  }
  
  Form::Form(const form_entry &fe)
      : short_name(fe.short_name), wiz_name(fe.wiz_name),
        duration(fe.duration),
        str_mod(fe.str_mod), dex_mod(fe.dex_mod),
        blocked_slots(fe.blocked_slots), size(fe.size), hp_mod(fe.hp_mod),
        can_cast(fe.can_cast), spellcasting_penalty(fe.spellcasting_penalty),
        unarmed_hit_bonus(fe.unarmed_hit_bonus), uc_colour(fe.uc_colour),
        uc_attack_verbs(fe.uc_attack_verbs),
        can_bleed(fe.can_bleed), breathes(fe.breathes),
        keeps_mutations(fe.keeps_mutations),
        shout_verb(fe.shout_verb),
        shout_volume_modifier(fe.shout_volume_modifier),
        hand_name(fe.hand_name), foot_name(fe.foot_name),
        flesh_equivalent(fe.flesh_equivalent),
        long_name(fe.long_name), description(fe.description),
        resists(fe.resists),
        base_unarmed_damage(fe.base_unarmed_damage),
        can_fly(fe.can_fly), can_swim(fe.can_swim),
        flat_ac(fe.flat_ac), power_ac(fe.power_ac), xl_ac(fe.xl_ac),
        uc_brand(fe.uc_brand), uc_attack(fe.uc_attack),
        prayer_action(fe.prayer_action), equivalent_mons(fe.equivalent_mons)
  { }
  
  Form::Form(transformation tran)
      : Form(_find_form_entry(tran))
  { }
  /**
   * Is the given equipment slot available for use in this form?
   *
   * @param slot      The equipment slot in question. (May be a weird fake
   *                  slot - EQ_STAFF or EQ_ALL_ARMOUR.)
   * @return          Whether at least some items can be worn in this slot in
   *                  this form.
   *                  (The player's race, or mutations, may still block the
   *                  slot, or it may be restricted to subtypes.)
   */
  bool Form::slot_available(int slot) const
  {
      if (slot == EQ_ALL_ARMOUR)
          return !all_blocked(EQF_WEAR);
      if (slot == EQ_RINGS || slot == EQ_RINGS_PLUS)
          return !all_blocked(EQF_RINGS);
  
      if (slot == EQ_STAFF)
          slot = EQ_WEAPON;
      return !(blocked_slots & SLOTF(slot));
  }
  
  /**
   * Can the player wear the given item while in this form?
   *
   * Does not take mutations into account.
   *
   * @param item  The item in question
   * @return      Whether this form prevents the player from wearing the
   *              item. (Other things may also prevent it, of course)
   */
  bool Form::can_wear_item(const item_def& item) const
  {
      if (item.base_type == OBJ_JEWELLERY)
      {
          if (jewellery_is_amulet(item))
              return slot_available(EQ_AMULET);
          return !all_blocked(EQF_RINGS);
      }
  
      if (is_unrandom_artefact(item, UNRAND_LEAR))
          return !(blocked_slots & EQF_LEAR); // ok if no body slots blocked
  
      return slot_available(get_armour_slot(item));
  }
  
  /**
   * Get the bonus to form duration granted for a given (spell)power.
   *
   * @param pow               The spellpower/equivalent of the form.
   * @return                  A bonus to form duration.
   */
  int FormDuration::power_bonus(int pow) const
  {
      switch (scaling_type)
      {
          case PS_NONE:
              return 0;
          case PS_SINGLE:
              return random2(pow);
          case PS_ONE_AND_A_HALF:
              return random2(pow) + random2(pow/2);
          case PS_DOUBLE:
              return random2(pow) + random2(pow);
          default:
              die("Unknown scaling type!");
              return -1;
      }
  }
  
  /**
   * Get the duration for this form, when newly entered.
   *
   * @param pow   The power of the effect creating this form. (Spellpower, etc.)
   * @return      The duration of the form. (XXX: in turns...?)
   */
  int Form::get_duration(int pow) const
  {
      return min(duration.base + duration.power_bonus(pow), duration.max);
  }
  
  /**
   * Get a verbose description for the form.
   *
   * @param past_tense     Whether the description should be in past or present
   *                       tense.
   * @return               A description for the form.
   */
  string Form::get_description(bool past_tense) const
  {
      return make_stringf("You %s %s",
                          past_tense ? "were" : "are",
                          description.c_str());
  }
  
  /**
   * Get a message for transforming into this form, based on your current
   * situation (e.g. in water...)
   *
   * @return The message for turning into this form.
   */
  string Form::transform_message(transformation previous_trans) const
  {
      // XXX: refactor this into a second function (and also rethink the logic)
      string start = "Buggily, y";
      if (you.in_water() && player_can_fly())
          start = "You fly out of the water as y";
      else if (get_form(previous_trans)->player_can_fly()
               && player_can_swim()
               && feat_is_water(grd(you.pos())))
          start = "As you dive into the water, y";
      else
          start = "Y";
  
      return make_stringf("%sou turn into %s", start.c_str(),
                          get_transform_description().c_str());
  }
  
  /**
   * Get a message for untransforming from this form.
   *
   * @return "Your transform has ended."
   */
  string Form::get_untransform_message() const
  {
      return "Your transformation has ended.";
  }
  
  /**
   * What AC bonus does the player get while in this form?
   *
   * Many forms are power-dependent, so the answer given may be strange if the
   * player isn't currently in the form in question.
   *
   * @return  The AC bonus currently granted by the form, multiplied by 100 to
   *          allow for pseudo-decimal flexibility (& to match
   *          player::armour_class())
   */
  int Form::get_ac_bonus() const
  {
      return flat_ac * 100
             + power_ac * you.props[TRANSFORM_POW_KEY].get_int()
             + xl_ac * you.experience_level;
  }
  
  /**
   * (freeze)
   */
  static string _brand_suffix(brand_type brand)
  {
      if (brand == SPWPN_NORMAL)
          return "";
      return make_stringf(" (%s)", brand_type_name(brand, true));
  }
  
  /**
   * What name should be used for the player's means of unarmed attack while
   * in this form?
   *
   * (E.g. for display in the top-right of the UI.)
   *
   * @param   The player's UC weapon when not in a form (claws, etc)
   * @return  A string describing the form's UC attack 'weapon'.
   */
  string Form::get_uc_attack_name(string default_name) const
  {
      const string brand_suffix = _brand_suffix(get_uc_brand());
      if (uc_attack.empty())
          return default_name + brand_suffix;
      return uc_attack + brand_suffix;
  }
  
  /**
   * How many levels of resistance against fire does this form provide?
   */
  int Form::res_fire() const
  {
      return get_resist(resists, MR_RES_FIRE);
  }
  
  /**
   * How many levels of resistance against cold does this form provide?
   */
  int Form::res_cold() const
  {
      return get_resist(resists, MR_RES_COLD);
  }
  
  /**
   * How many levels of resistance against negative energy does this form give?
   */
  int Form::res_neg() const
  {
      return get_resist(resists, MR_RES_NEG);
  }
  
  /**
   * Does this form provide resistance to electricity?
   */
  bool Form::res_elec() const
  {
      return get_resist(resists, MR_RES_ELEC);
  }
  
  /**
   * How many levels of resistance against poison does this form give?
   */
  int Form::res_pois() const
  {
      return get_resist(resists, MR_RES_POISON);
  }
  
  /**
   * Does this form provide resistance to rotting?
   */
  bool Form::res_rot() const
  {
      return get_resist(resists, MR_RES_ROTTING);
  }
  
  /**
   * Does this form provide resistance against acid?
   */
  bool Form::res_acid() const
  {
      return get_resist(resists, MR_RES_ACID);
  }
  
  /**
   * Does this form provide resistance to sticky flame?
   */
  bool Form::res_sticky_flame() const
  {
      return get_resist(resists, MR_RES_STICKY_FLAME);
  }
  
  /**
   * Does this form provide resistance to petrification?
   */
  bool Form::res_petrify() const
  {
      return get_resist(resists, MR_RES_PETRIFY);
  }
  
  
  /**
   * Does this form enable flight?
   *
   * @return Whether this form allows flight for characters which don't already
   *         have access to it.
   */
  bool Form::enables_flight() const
  {
      return can_fly == FC_ENABLE;
  }
  
  /**
   * Does this form disable flight?
   *
   * @return Whether flight is always impossible while in this form.
   */
  bool Form::forbids_flight() const
  {
      return can_fly == FC_FORBID;
  }
  
  /**
   * Does this form disable swimming?
   *
   * @return Whether swimming is always impossible while in this form.
   */
  bool Form::forbids_swimming() const
  {
      return can_swim == FC_FORBID;
  }
  
  /**
   * Can the player fly, if in this form?
   *
   * DOES consider player state besides form.
   * @return  Whether the player will be able to fly in this form.
   */
  bool Form::player_can_fly() const
  {
      return !forbids_flight()
             && (enables_flight()
                 || you.racial_permanent_flight() && you.permanent_flight());
  }
  
  /**
   * Can the player swim, if in this form?
   *
   * DOES consider player state besides form.
   * @return  Whether the player will be able to swim in this form.
   */
  bool Form::player_can_swim() const
  {
      const size_type player_size = size == SIZE_CHARACTER ?
                                            you.body_size(PSIZE_BODY, true) :
                                            size;
      return can_swim == FC_ENABLE
             || species_can_swim(you.species) && can_swim != FC_FORBID
             || player_size >= SIZE_GIANT;
  }
  
  /**
   * Are all of the given equipment slots blocked while in this form?
   *
   * @param slotflags     A set of flags, corresponding to the union of
   (1 << the slot enum) for each slot in question.
   * @return              Whether all of the given slots are blocked.
   */
  bool Form::all_blocked(int slotflags) const
  {
      return slotflags == (blocked_slots & slotflags);
  }
  
  /**
   * What message should be printed when the player prays at an altar?
   * To be inserted into "You %s the altar of foo."
   *
   * If the form has a valid custom action, print that; otherwise, default to the
   * 'flying' or species-specific actions, as appropriate.
   *
   * @return  An action to be printed when the player prays at an altar.
   *          E.g., "perch on", "crawl onto", "sway towards", etc.
   */
  string Form::player_prayer_action() const
  {
      // If the form is naturally flying & specifies an action, use that.
      if (can_fly == FC_ENABLE && !prayer_action.empty())
          return prayer_action;
      // Otherwise, if you're flying, use the generic flying action.
      // XXX: if we ever get a default-permaflying species again that wants to
      // have a separate verb, we'll want to check for that right here.
      if (you.airborne())
          return "hover solemnly before";
      // Otherwise, if you have a verb, use that...
      if (!prayer_action.empty())
          return prayer_action;
      // Finally, default to your species' verb.
      return species_prayer_action(you.species);
  }
  
  class FormNone : public Form
  {
  private:
      FormNone() : Form(transformation::none) { }
      DISALLOW_COPY_AND_ASSIGN(FormNone);
  public:
      static const FormNone &instance() { static FormNone inst; return inst; }
  
      /**
       * Get a string describing the form you're turning into. (If not the same
       * as the one used to describe this form in @.
       */
      string get_transform_description() const override { return "your old self."; }
  };
  
  class FormSpider : public Form
  {
  private:
      FormSpider() : Form(transformation::spider) { }
      DISALLOW_COPY_AND_ASSIGN(FormSpider);
  public:
      static const FormSpider &instance() { static FormSpider inst; return inst; }
  };
  
  class FormBlade : public Form
  {
  private:
      FormBlade() : Form(transformation::blade_hands) { }
      DISALLOW_COPY_AND_ASSIGN(FormBlade);
  public:
      static const FormBlade &instance() { static FormBlade inst; return inst; }
  
      /**
       * % screen description
       */
      string get_long_name() const override
      {
          return "blade " + blade_parts(true);
      }
  
      /**
       * @ description
       */
      string get_description(bool past_tense) const override
      {
          return make_stringf("You %s blades for %s.",
                              past_tense ? "had" : "have",
                              blade_parts().c_str());
      }
  
      /**
       * Get a message for transforming into this form.
       */
      string transform_message(transformation previous_trans) const override
      {
          const bool singular = you.get_mutation_level(MUT_MISSING_HAND);
  
          // XXX: a little ugly
          return make_stringf("Your %s turn%s into%s razor-sharp scythe blade%s.",
                              blade_parts().c_str(), singular ? "s" : "",
                              singular ? " a" : "", singular ? "" : "s");
      }
  
      /**
       * Get a message for untransforming from this form.
       */
      string get_untransform_message() const override
      {
          const bool singular = you.get_mutation_level(MUT_MISSING_HAND);
  
          // XXX: a little ugly
          return make_stringf("Your %s revert%s to %s normal proportions.",
                              blade_parts().c_str(), singular ? "s" : "",
                              singular ? "its" : "their");
      }
  
      bool can_offhand_punch() const override { return true; }
  
      /**
       * Get the name displayed in the UI for the form's unarmed-combat 'weapon'.
       */
      string get_uc_attack_name(string default_name) const override
      {
          return "Blade " + blade_parts(true);
      }
  };
  
  class FormStatue : public Form
  {
  private:
      FormStatue() : Form(transformation::statue) { }
      DISALLOW_COPY_AND_ASSIGN(FormStatue);
  public:
      static const FormStatue &instance() { static FormStatue inst; return inst; }
  
      /**
       * Get a message for transforming into this form.
       */
      string transform_message(transformation previous_trans) const override
      {
          if (you.species == SP_DEEP_DWARF && one_chance_in(10))
              return "You inwardly fear your resemblance to a lawn ornament.";
          else if (you.species == SP_GARGOYLE)
              return "Your body stiffens and grows slower.";
          else
              return Form::transform_message(previous_trans);
      }
  
      /**
       * Get a string describing the form you're turning into. (If not the same
       * as the one used to describe this form in @.
       */
      string get_transform_description() const override
      {
          return "a living statue of rough stone.";
      }
  
      /**
       * Get a message for untransforming from this form.
       */
      string get_untransform_message() const override
      {
          // This only handles lava orcs going statue -> stoneskin.
          if (you.species == SP_GARGOYLE)
              return "You revert to a slightly less stony form.";
          return "You revert to your normal fleshy form.";
      }
  
      /**
       * Get the name displayed in the UI for the form's unarmed-combat 'weapon'.
       */
      string get_uc_attack_name(string default_name) const override
      {
          if (you.has_usable_claws(true))
              return "Stone claws";
          if (you.has_usable_tentacles(true))
              return "Stone tentacles";
  
          const bool singular = you.get_mutation_level(MUT_MISSING_HAND);
          return make_stringf("Stone fist%s", singular ? "" : "s");
      }
  };
  
  class FormIce : public Form
  {
  private:
      FormIce() : Form(transformation::ice_beast) { }
      DISALLOW_COPY_AND_ASSIGN(FormIce);
  public:
      static const FormIce &instance() { static FormIce inst; return inst; }
  
      /**
       * Get a message for untransforming from this form.
       */
      string get_untransform_message() const override
      {
          return "You warm up again.";
      }
  
      /**
       * Get the name displayed in the UI for the form's unarmed-combat 'weapon'.
       */
      string get_uc_attack_name(string default_name) const override
      {
          const bool singular = you.get_mutation_level(MUT_MISSING_HAND);
          return make_stringf("Ice fist%s", singular ? "" : "s");
      }
  };
  
  class FormDragon : public Form
  {
  private:
      FormDragon() : Form(transformation::dragon) { }
      DISALLOW_COPY_AND_ASSIGN(FormDragon);
  public:
      static const FormDragon &instance() { static FormDragon inst; return inst; }
  
      /**
       * Get an monster type corresponding to the transformation.
       *
       * (Used for console player glyphs.)
       *
       * @return  A monster type corresponding to the player in this form.
       */
      monster_type get_equivalent_mons() const override
      {
          return dragon_form_dragon_type();
      }
  
      /**
       * The AC bonus of the form, multiplied by 100 to match
       * player::armour_class().
       */
      int get_ac_bonus() const override
      {
          if (species_is_draconian(you.species))
              return 1000;
          return Form::get_ac_bonus();
      }
  
      /**
       * How many levels of resistance against fire does this form provide?
       */
      int res_fire() const override
      {
          switch (dragon_form_dragon_type())
          {
              case MONS_FIRE_DRAGON:
                  return 2;
              case MONS_ICE_DRAGON:
                  return -1;
              default:
                  return 0;
          }
      }
  
      /**
       * How many levels of resistance against cold does this form provide?
       */
      int res_cold() const override
      {
          switch (dragon_form_dragon_type())
          {
              case MONS_ICE_DRAGON:
                  return 2;
              case MONS_FIRE_DRAGON:
                  return -1;
              default:
                  return 0;
          }
      }
  
      bool can_offhand_punch() const override { return true; }
  };
  
  class FormLich : public Form
  {
  private:
      FormLich() : Form(transformation::lich) { }
      DISALLOW_COPY_AND_ASSIGN(FormLich);
  public:
      static const FormLich &instance() { static FormLich inst; return inst; }
  
      /**
       * Get a message for transforming into this form.
       */
      string transform_message(transformation previous_trans) const override
      {
          return "Your body is suffused with negative energy!";
      }
  
      /**
       * Get a message for untransforming from this form.
       */
      string get_untransform_message() const override
      {
          if (you.undead_state() == US_ALIVE)
              return "You feel yourself come back to life.";
          return "You feel your undeath return to normal.";
          // ^^^ vampires only, probably
      }
  };
  
  class FormBat : public Form
  {
  private:
      FormBat() : Form(transformation::bat) { }
      DISALLOW_COPY_AND_ASSIGN(FormBat);
  public:
      static const FormBat &instance() { static FormBat inst; return inst; }
  
      /**
       * Get an monster type corresponding to the transformation.
       *
       * (Used for console player glyphs.)
       *
       * @return  A monster type corresponding to the player in this form.
       */
      monster_type get_equivalent_mons() const override
      {
          return you.species == SP_VAMPIRE ? MONS_VAMPIRE_BAT : MONS_BAT;
      }
  
      string get_description(bool past_tense) const override
      {
          return make_stringf("You %s in %sbat-form.",
                              past_tense ? "were" : "are",
                              you.species == SP_VAMPIRE ?  "vampire-" : "");
      }
  
      /**
       * Get a string describing the form you're turning into. (If not the same
       * as the one used to describe this form in @.
       */
      string get_transform_description() const override
      {
          return make_stringf("a %sbat.",
                              you.species == SP_VAMPIRE ? "vampire " : "");
      }
  };
  
  class FormPig : public Form
  {
  private:
      FormPig() : Form(transformation::pig) { }
      DISALLOW_COPY_AND_ASSIGN(FormPig);
  public:
      static const FormPig &instance() { static FormPig inst; return inst; }
  };
  
  class FormAppendage : public Form
  {
  private:
      FormAppendage() : Form(transformation::appendage) { }
      DISALLOW_COPY_AND_ASSIGN(FormAppendage);
  public:
      static const FormAppendage &instance()
      {
          static FormAppendage inst;
          return inst;
      }
  
      string get_description(bool past_tense) const override
      {
          if (you.attribute[ATTR_APPENDAGE] == MUT_TENTACLE_SPIKE)
          {
              return make_stringf("One of your tentacles %s a temporary spike.",
                                   past_tense ? "had" : "has");
          }
  
          return make_stringf("You %s grown temporary %s.",
                              past_tense ? "had" : "have",
                              mutation_name((mutation_type)
                                            you.attribute[ATTR_APPENDAGE]));
      }
  
      /**
       * Get a message for transforming into this form.
       */
      string transform_message(transformation previous_trans) const override
      {
          // ATTR_APPENDAGE must be set earlier!
          switch (you.attribute[ATTR_APPENDAGE])
          {
              case MUT_HORNS:
                  return "You grow a pair of large bovine horns.";
              case MUT_TENTACLE_SPIKE:
                  return "One of your tentacles grows a vicious spike.";
              case MUT_TALONS:
                  return "Your feet morph into talons.";
              default:
                   die("Unknown beastly appendage.");
          }
      }
  
      /**
       * Get a message for untransforming from this form. (Handled elsewhere.)
       */
      string get_untransform_message() const override { return ""; }
  };
  
  class FormTree : public Form
  {
  private:
      FormTree() : Form(transformation::tree) { }
      DISALLOW_COPY_AND_ASSIGN(FormTree);
  public:
      static const FormTree &instance() { static FormTree inst; return inst; }
  
      /**
       * Get a message for untransforming from this form.
       */
      string get_untransform_message() const override { return "You feel less wooden."; }
  };
  
  #if TAG_MAJOR_VERSION == 34
  class FormPorcupine : public Form
  {
  private:
      FormPorcupine() : Form(transformation::porcupine) { }
      DISALLOW_COPY_AND_ASSIGN(FormPorcupine);
  public:
      static const FormPorcupine &instance()
      {
          static FormPorcupine inst;
          return inst;
      }
  };
  #endif
  
  class FormWisp : public Form
  {
  private:
      FormWisp() : Form(transformation::wisp) { }
      DISALLOW_COPY_AND_ASSIGN(FormWisp);
  public:
      static const FormWisp &instance() { static FormWisp inst; return inst; }
  };
  
  #if TAG_MAJOR_VERSION == 34
  class FormJelly : public Form
  {
  private:
      FormJelly() : Form(transformation::jelly) { }
      DISALLOW_COPY_AND_ASSIGN(FormJelly);
  public:
      static const FormJelly &instance() { static FormJelly inst; return inst; }
  };
  #endif
  
  class FormFungus : public Form
  {
  private:
      FormFungus() : Form(transformation::fungus) { }
      DISALLOW_COPY_AND_ASSIGN(FormFungus);
  public:
      static const FormFungus &instance() { static FormFungus inst; return inst; }
  
      /**
       * Get a message for untransforming from this form.
       */
      string get_untransform_message() const override { return "You stop sporulating."; }
  };
  
  class FormShadow : public Form
  {
  private:
      FormShadow() : Form(transformation::shadow) { }
      DISALLOW_COPY_AND_ASSIGN(FormShadow);
  public:
      static const FormShadow &instance() { static FormShadow inst; return inst; }
  
      /**
       * Get a message for untransforming from this form.
       */
      string get_untransform_message() const override
      {
          if (you.invisible())
              return "You feel less shadowy.";
          return "You emerge from the shadows.";
      }
  };
  
  /**
   * Set the number of hydra heads that the player currently has.
   *
   * @param heads the new number of heads you should have.
   */
  void set_hydra_form_heads(int heads)
  {
      you.props[HYDRA_FORM_HEADS_KEY] = min(MAX_HYDRA_HEADS, max(1, heads));
      you.wield_change = true;
  }
  
  class FormHydra : public Form
  {
  private:
      FormHydra() : Form(transformation::hydra) { }
      DISALLOW_COPY_AND_ASSIGN(FormHydra);
  public:
      static const FormHydra &instance() { static FormHydra inst; return inst; }
  
      /**
       * Get a string describing the form you're turning into.
       */
      string get_transform_description() const override
      {
          const auto heads = you.heads();
          const string headstr = (heads < 11 ? number_in_words(heads)
                                             : to_string(heads))
                               + "-headed hydra.";
          return article_a(headstr);
      }
  
      /**
       * @ description
       */
      string get_description(bool past_tense) const override
      {
          return make_stringf("You %s %s",
                              past_tense ? "were" : "are",
                              get_transform_description().c_str());
      }
  
      /**
       * Get the name displayed in the UI for the form's unarmed-combat 'weapon'.
       */
      string get_uc_attack_name(string default_name) const override
      {
          return make_stringf("Bite (x%d)", you.heads());
      }
  
      /**
       * Find the player's base unarmed damage in this form.
       */
      int get_base_unarmed_damage() const override
      {
          // 3 damage per head for 1-10
          const int normal_heads_damage = min(you.heads(), 10) * 3;
          // 3/2 damage per head for 11-20 (they get in each-other's way)
              // (and also a 62-base-damage form scares me)
          const int too_many_heads_damage = max(0, you.heads() - 10)
                                              * 3 / 2;
          // 2-47 (though more like 14-32 in practical ranges...)
          return 2 + normal_heads_damage + too_many_heads_damage;
      }
  
  };
  
  static const Form* forms[] =
  {
      &FormNone::instance(),
      &FormSpider::instance(),
      &FormBlade::instance(),
      &FormStatue::instance(),
  
      &FormIce::instance(),
      &FormDragon::instance(),
      &FormLich::instance(),
      &FormBat::instance(),
  
      &FormPig::instance(),
      &FormAppendage::instance(),
      &FormTree::instance(),
      &FormPorcupine::instance(),
  
      &FormWisp::instance(),
  #if TAG_MAJOR_VERSION == 34
      &FormJelly::instance(),
  #endif
      &FormFungus::instance(),
      &FormShadow::instance(),
      &FormHydra::instance(),
  };
  
  const Form* get_form(transformation xform)
  {
      COMPILE_CHECK(ARRAYSZ(forms) == NUM_TRANSFORMS);
      const int form = static_cast<int>(xform);
      ASSERT_RANGE(form, 0, NUM_TRANSFORMS);
      return forms[form];
  }
  
  
  /**
   * Get the wizmode name of a form.
   *
   * @param form      The form in question.
   * @return          The form's casual, wizmode name.
   */
  const char* transform_name(transformation form)
  {
      return get_form(form)->wiz_name.c_str();
  }
  
  /**
   * Can the player (w)ield weapons when in the given form?
   *
   * @param form      The form in question.
   * @return          Whether the player can wield items when in that form.
  */
  bool form_can_wield(transformation form)
  {
      return get_form(form)->can_wield();
  }
  
  /**
   * Can the player (W)ear armour when in the given form?
   *
   * @param form      The form in question.
   * @return          Whether the player can wear armour when in that form.
   */
  bool form_can_wear(transformation form)
  {
      return !testbits(get_form(form)->blocked_slots, EQF_WEAR);
  }
  
  /**
   * Can the player fly, if in this form?
   *
   * DOES consider player state besides form.
   * @param form      The form in question.
   * @return          Whether the player will be able to fly in this form.
   */
  bool form_can_fly(transformation form)
  {
      return get_form(form)->player_can_fly();
  }
  
  
  /**
   * Can the player swim, if in this form?
   *
   * (Swimming = traversing deep & shallow water without penalties; includes
   * floating (ice form) and wading forms (giants - currently just dragon form,
   * which normally flies anyway...))
   *
   * DOES consider player state besides form.
   * @param form      The form in question.
   * @return          Whether the player will be able to swim in this form.
   */
  bool form_can_swim(transformation form)
  {
      return get_form(form)->player_can_swim();
  }
  
  /**
   * Can the player survive in deep water when in the given form?
   *
   * Doesn't count flight or beogh water-walking.
   *
   * @param form      The form in question.
   * @return          Whether the player won't be killed when entering deep water
   *                  in that form.
   */
  bool form_likes_water(transformation form)
  {
      // Grey dracs can't swim, so can't statue form merfolk/octopodes
      // -- yet they can still survive in water.
      if (species_likes_water(you.species)
          && (form == transformation::statue
              || !get_form(form)->forbids_swimming()))
      {
          return true;
      }
  
      // otherwise, you gotta swim to survive!
      return form_can_swim(form);
  }
  
  // Used to mark transformations which override species intrinsics.
  bool form_changed_physiology(transformation form)
  {
      return form != transformation::none
          && form != transformation::appendage
          && form != transformation::blade_hands;
  }
  
  /**
   * Does this form have blood?
   *
   * @param form      The form in question.
   * @return          Whether the form can bleed, sublime, etc.
   */
  bool form_can_bleed(transformation form)
  {
      return get_form(form)->can_bleed != FC_FORBID;
  }
  
  // Used to mark forms which keep most form-based mutations.
  bool form_keeps_mutations(transformation form)
  {
      return get_form(form)->keeps_mutations;
  }
  
  static set<equipment_type>
  _init_equipment_removal(transformation form)
  {
      set<equipment_type> result;
      if (!form_can_wield(form) && you.weapon() || you.melded[EQ_WEAPON])
          result.insert(EQ_WEAPON);
  
      // Liches can't wield holy weapons.
      if (form == transformation::lich && you.weapon()
          && is_holy_item(*you.weapon()))
      {
          result.insert(EQ_WEAPON);
      }
  
      for (int i = EQ_FIRST_EQUIP; i < NUM_EQUIP; ++i)
      {
          if (i == EQ_WEAPON)
              continue;
          const equipment_type eq = static_cast<equipment_type>(i);
          const item_def *pitem = you.slot_item(eq, true);
  
          if (pitem && (get_form(form)->blocked_slots & SLOTF(i)
                        || (i != EQ_RING_AMULET
                            && !get_form(form)->can_wear_item(*pitem))))
          {
              result.insert(eq);
          }
      }
      return result;
  }
  
  static void _remove_equipment(const set<equipment_type>& removed,
                                transformation form,
                                bool meld = true, bool mutation = false)
  {
  	// addedcrawl : For unrand_dracomonk
  	if (player_equip_unrand(UNRAND_DRACOMONK) || you.duration[DUR_DRACOMONK])
  	{
  		you.duration[DUR_DRACOMONK] = INFINITE_DURATION;
  	}
  	
      // Meld items into you in (reverse) order. (set is a sorted container)
      for (const equipment_type e : removed)
      {
          item_def *equip = you.slot_item(e, true);
          if (equip == nullptr)
              continue;
  
          bool unequip = !meld;
          if (!unequip && e == EQ_WEAPON)
          {
              if (form_can_wield(form))
                  unequip = true;
              if (!is_weapon(*equip))
                  unequip = true;
          }
  
          mprf("%s %s%s %s", equip->name(DESC_YOUR).c_str(),
               unequip ? "fall" : "meld",
               equip->quantity > 1 ? "" : "s",
               unequip ? "away!" : "into your body.");
  
          if (unequip)
          {
              if (e == EQ_WEAPON)
              {
                  unwield_item(!you.berserk());
                  canned_msg(MSG_EMPTY_HANDED_NOW);
              }
              else
                  unequip_item(e);
  
              if (mutation)
              {
                  // A mutation made us not only lose an equipment slot
                  // but actually removed a worn item: Funny!
                  xom_is_stimulated(is_artefact(*equip) ? 200 : 100);
              }
          }
          else
              meld_slot(e);
      }
  
      if (meld)
      {
          for (const equipment_type e : removed)
              if (you.slot_item(e, true) != nullptr)
                  unequip_effect(e, you.equip[e], true, true);
      }
  }
  
  static void _unmeld_equipment_type(equipment_type e)
  {
      item_def& item = you.inv[you.equip[e]];
      bool force_remove = false;
  
      if (e == EQ_WEAPON)
      {
          if (you.slot_item(EQ_SHIELD)
              && is_shield_incompatible(item, you.slot_item(EQ_SHIELD)))
          {
              force_remove = true;
          }
      }
      else if (item.base_type != OBJ_JEWELLERY)
      {
          // This could happen if the player was mutated during the form.
          if (!can_wear_armour(item, false, false))
              force_remove = true;
  
          // If you switched weapons during the transformation, make
          // sure you can still wear your shield.
          // (This is only possible with Statue Form.)
          if (e == EQ_SHIELD && you.weapon()
              && is_shield_incompatible(*you.weapon(), &item))
          {
              force_remove = true;
          }
      }
  
      if (force_remove)
      {
          mprf("%s is pushed off your body!", item.name(DESC_YOUR).c_str());
          unequip_item(e);
      }
      else
      {
          mprf("%s unmelds from your body.", item.name(DESC_YOUR).c_str());
          unmeld_slot(e);
      }
  }
  
  static void _unmeld_equipment(const set<equipment_type>& melded)
  {
  	// addedcrawl : For unrand_dracomonk
  	if (you.duration[DUR_DRACOMONK])
  	{
  		you.duration[DUR_DRACOMONK] = 0;
  	}
  	
      // Unmeld items in order.
      for (const equipment_type e : melded)
      {
          if (you.equip[e] == -1)
              continue;
  
          _unmeld_equipment_type(e);
      }
  
      for (const equipment_type e : melded)
          if (you.equip[e] != -1)
              equip_effect(e, you.equip[e], true, true);
  }
  
  void unmeld_one_equip(equipment_type eq)
  {
      if (eq >= EQ_HELMET && eq <= EQ_BOOTS)
      {
          const item_def* arm = you.slot_item(EQ_BODY_ARMOUR, true);
          if (arm && is_unrandom_artefact(*arm, UNRAND_LEAR))
              eq = EQ_BODY_ARMOUR;
      }
  
      set<equipment_type> e;
      e.insert(eq);
      _unmeld_equipment(e);
  }
  
  void remove_one_equip(equipment_type eq, bool meld, bool mutation)
  {
      if (player_equip_unrand(UNRAND_LEAR) && eq >= EQ_HELMET && eq <= EQ_BOOTS)
          eq = EQ_BODY_ARMOUR;
  
      set<equipment_type> r;
      r.insert(eq);
      _remove_equipment(r, you.form, meld, mutation);
  }
  
  /**
   * Get an monster type corresponding to the player's current form.
   *
   * (Used for console player glyphs.)
   *
   * @return  A monster type corresponding to the player in the form.
   */
  monster_type transform_mons()
  {
      return get_form()->get_equivalent_mons();
  }
  
  string blade_parts(bool terse)
  {
      string str;
  
      if (you.species == SP_FELID)
          str = terse ? "paw" : "front paw";
      else if (you.species == SP_OCTOPODE)
          str = "tentacle";
      else
          str = "hand";
  
      if (!you.get_mutation_level(MUT_MISSING_HAND))
          str = pluralise(str);
  
      return str;
  }
  
  // with a denominator of 10
  int form_hp_mod()
  {
      return get_form()->hp_mod;
  }
  
  static bool _flying_in_new_form(transformation which_trans)
  {
      if (get_form(which_trans)->forbids_flight())
          return false;
  
      // If our flight is uncancellable (or tenguish) then it's not from evoking
      if (you.attribute[ATTR_FLIGHT_UNCANCELLABLE]
          || you.permanent_flight() && you.racial_permanent_flight())
      {
          return true;
      }
  
      if (!you.duration[DUR_FLIGHT] && !you.attribute[ATTR_PERM_FLIGHT])
          return false;
  
      int sources = you.evokable_flight();
      int sources_removed = 0;
      for (auto eq : _init_equipment_removal(which_trans))
      {
          item_def *item = you.slot_item(eq, true);
          if (item == nullptr)
              continue;
          item_info inf = get_item_info(*item);
  
          //similar code to safe_to_remove from item-use.cc
          if (inf.is_type(OBJ_JEWELLERY, RING_FLIGHT))
              sources_removed++;
          if (inf.base_type == OBJ_ARMOUR && inf.brand == SPARM_FLYING)
              sources_removed++;
          if (is_artefact(inf) && artefact_known_property(inf, ARTP_FLY))
              sources_removed++;
      }
  
      return sources > sources_removed;
  }
  
  /**
   * Check if it'd be lethal for the player to enter a form in a given terrain.
   *
   * In addition to checking whether the feature is dangerous for the form
   * itself (form_likes_*), the function checks to see if the player is safe
   * due to flying or similar effects.
   *
   * @param which_trans       The form being checked.
   * @param feat              The dungeon feature to be checked for danger.
   * @return                  If the feat is lethal for the player in the form.
   */
  bool feat_dangerous_for_form(transformation which_trans,
                               dungeon_feature_type feat)
  {
      // Everything is okay if we can fly.
      if (form_can_fly(which_trans) || _flying_in_new_form(which_trans))
          return false;
  
      if (feat == DNGN_LAVA)
          return true;
  
      if (feat == DNGN_DEEP_WATER)
          return !you.can_water_walk() && !form_likes_water(which_trans);
  
      return false;
  }
  
  static mutation_type appendages[] =
  {
      MUT_HORNS,
      MUT_TENTACLE_SPIKE,
      MUT_TALONS,
  };
  
  static bool _slot_conflict(equipment_type eq)
  {
      // Choose uncovered slots only. Melding could make people re-cast
      // until they get something that doesn't conflict with their randart
      // of überness.
      if (you.equip[eq] != -1)
      {
          // Horns + hat is fine.
          if (eq != EQ_HELMET
              || you.melded[eq]
              || is_hard_helmet(*(you.slot_item(eq))))
          {
              return true;
          }
      }
  
      for (int mut = 0; mut < NUM_MUTATIONS; mut++)
          if (you.has_mutation(static_cast<mutation_type>(mut)) && eq == beastly_slot(mut))
              return true;
  
      return false;
  }
  
  static mutation_type _beastly_appendage()
  {
      mutation_type chosen = NUM_MUTATIONS;
      int count = 0;
  
      for (mutation_type app : appendages)
      {
          if (_slot_conflict(beastly_slot(app)))
              continue;
          if (physiology_mutation_conflict(app))
              continue;
  
          if (one_chance_in(++count))
              chosen = app;
      }
      return chosen;
  }
  
  static bool _transformation_is_safe(transformation which_trans,
                                      dungeon_feature_type feat,
                                      string *fail_reason)
  {
      if (!feat_dangerous_for_form(which_trans, feat))
          return true;
  
      if (fail_reason)
      {
          *fail_reason = make_stringf("You would %s in your new form.",
                                      feat == DNGN_DEEP_WATER ? "drown" : "burn");
      }
  
      return false;
  }
  
  /**
   * If we transform into the given form, will all of our stats remain above 0,
   * based purely on the stat modifiers of the current & destination form?
   *
   * May prompt the player.
   *
   * @param new_form  The form to check the safety of.
   * @param quiet     Whether to prompt the player.
   * @return          Whether it's okay to go ahead with the transformation.
   */
  bool check_form_stat_safety(transformation new_form, bool quiet)
  {
      const int str_mod = get_form(new_form)->str_mod - get_form()->str_mod;
      const int dex_mod = get_form(new_form)->dex_mod - get_form()->dex_mod;
  
      const bool bad_str = you.strength() > 0 && str_mod + you.strength() <= 0;
      const bool bad_dex = you.dex() > 0 && dex_mod + you.dex() <= 0;
      if (!bad_str && !bad_dex)
          return true;
      if (quiet)
          return false;
  
      string prompt = make_stringf("%s will reduce your %s to zero. Continue?",
                                   new_form == transformation::none
                                       ? "Turning back"
                                       : "Transforming",
                                   bad_str ? "strength" : "dexterity");
      if (yesno(prompt.c_str(), false, 'n'))
          return true;
  
      canned_msg(MSG_OK);
      return false;
  }
  
  static int _transform_duration(transformation which_trans, int pow)
  {
      return get_form(which_trans)->get_duration(pow);
  }
  
  static int _beastly_appendage_level(int appendage)
  {
      switch (appendage)
      {
      case MUT_HORNS: return 2;
      default:        return 3;
      }
  }
  
  /**
   * Print an appropriate message when the number of heads the player has
   * changes during a refresh of hydra form.
   */
  static void _print_head_change_message(int old_heads, int new_heads)
  {
      if (old_heads == new_heads)
          return;
  
      const int delta = abs(old_heads - new_heads);
      const bool plural = delta != 1;
      if (old_heads > new_heads)
      {
          if (plural)
              mprf("%d of your heads shrink away.", delta);
          else
              mpr("One of your heads shrinks away.");
          return;
      }
  
      if (plural)
          mprf("%d new heads grow.", delta);
      else
          mpr("A new head grows.");
  }
  
  /**
   * Is the player alive enough to become the given form?
   *
   * All undead can enter shadow form; vampires also can enter batform, and, when
   * full, other forms (excepting lichform).
   *
   * @param which_trans   The tranformation which the player is undergoing
   *                      (default you.form).
   * @param involuntary   Whether the transformation is involuntary or not.
   * @return              UFR_GOOD if the player is not blocked from entering the
   *                      given form by their undead race; UFR_TOO_ALIVE if the
   *                      player is too satiated as a vampire; UFR_TOO_DEAD if
   *                      the player is too dead (or too thirsty as a vampire).
   */
  undead_form_reason lifeless_prevents_form(transformation which_trans,
                                            bool involuntary)
  {
      if (!you.undead_state(false))
          return UFR_GOOD; // not undead!
  
      if (which_trans == transformation::none)
          return UFR_GOOD; // everything can become itself
  
      if (which_trans == transformation::shadow)
          return UFR_GOOD; // even the undead can use dith's shadow form
  
      if (you.species != SP_VAMPIRE)
          return UFR_TOO_DEAD; // ghouls & mummies can't become anything else
  
      if (which_trans == transformation::lich)
          return UFR_TOO_DEAD; // vampires can never lichform
  
      if (which_trans == transformation::bat) // can batform on satiated or below
      {
          if (involuntary)
              return UFR_TOO_DEAD; // but not as a forced polymorph effect
  
          return you.hunger_state <= HS_SATIATED ? UFR_GOOD : UFR_TOO_ALIVE;
      }
  
      // other forms can only be entered when satiated or above.
      return you.hunger_state >= HS_SATIATED ? UFR_GOOD : UFR_TOO_DEAD;
  }
  
  /**
   * Attempts to transform the player into the specified form.
   *
   * If the player is already in that form, attempt to refresh its duration and
   * power.
   *
   * @param pow               Thw power of the transformation (equivalent to
   *                          spellpower of form spells)
   * @param which_trans       The form which the player should become.
   * @param involuntary       Checks for inscription warnings are skipped, and
   *                          failure is silent.
   * @param just_check        A dry run; just check to see whether the player
   *                          *can* enter the given form, but don't actually
   *                          transform them.
   * @return                  If the player was transformed, or if they were
   *                          already in the given form, returns true.
   *                          Otherwise, false.
   *                          If just_check is set, returns true if the player
   *                          could enter the form (or is in it already) and
   *                          false otherwise.
   *                          N.b. that transform() can fail even when a
   *                          just_check run returns true; e.g. when Zin decides
   *                          to intervene. (That may be the only case.)
   */
  bool transform(int pow, transformation which_trans, bool involuntary,
                 bool just_check, string *fail_reason)
  {
      const transformation previous_trans = you.form;
      const bool was_flying = you.airborne();
      bool success = true;
      string msg;
  
      // Zin's protection.
      if (!just_check && have_passive(passive_t::resist_polymorph)
          && x_chance_in_y(you.piety, MAX_PIETY)
          && which_trans != transformation::none)
      {
          simple_god_message(" protects your body from unnatural transformation!");
          return false;
      }
  
      if (!involuntary && crawl_state.is_god_acting())
          involuntary = true;
  
      if (you.transform_uncancellable)
      {
          msg = "You are stuck in your current form!";
          success = false;
      }
      else if (!_transformation_is_safe(which_trans, env.grid(you.pos()), &msg))
          success = false;
  
      if (!success)
      {
          // Message is not printed if we're updating fail_reason.
          if (fail_reason)
              *fail_reason = msg;
          else if (!involuntary)
              mpr(msg);
          return false;
      }
  
      // This must occur before the untransform() and the undead_state() check.
      if (previous_trans == which_trans)
      {
          if (just_check)
              return true;
  
          // update power
          if (which_trans != transformation::none)
          {
              you.props[TRANSFORM_POW_KEY] = pow;
              you.redraw_armour_class = true;
              // ^ could check more carefully for the exact cases, but I'm
              // worried about making the code too fragile
  
              if (which_trans == transformation::hydra)
              {
                  const int heads = you.heads();
                  set_hydra_form_heads(div_rand_round(pow, 10));
                  _print_head_change_message(heads, you.heads());
              }
          }
  
          int dur = _transform_duration(which_trans, pow);
          if (you.duration[DUR_TRANSFORMATION] < dur * BASELINE_DELAY)
          {
              mpr("You extend your transformation's duration.");
              you.duration[DUR_TRANSFORMATION] = dur * BASELINE_DELAY;
  
          }
          else if (!involuntary && which_trans != transformation::none)
              mpr("You fail to extend your transformation any further.");
  
          return true;
      }
  
      // the undead cannot enter most forms.
      if (lifeless_prevents_form(which_trans, involuntary) == UFR_TOO_DEAD)
      {
          msg = "Your unliving flesh cannot be transformed in this way.";
          success = false;
      }
      else if (which_trans == transformation::lich
               && you.duration[DUR_DEATHS_DOOR])
      {
          msg = "You cannot become a lich while in Death's Door.";
          success = false;
      }
  
      if (!just_check && previous_trans != transformation::none)
          untransform(true);
  
      set<equipment_type> rem_stuff = _init_equipment_removal(which_trans);
  
      // if going into lichform causes us to drop a holy weapon with consequences
      // for unwielding (e.g. contam), warn first.
      item_def nil_item;
      nil_item.link = -1;
      if (just_check && !involuntary
          && which_trans == transformation::lich && rem_stuff.count(EQ_WEAPON)
          && !check_old_item_warning(nil_item, OPER_WIELD))
      {
          canned_msg(MSG_OK);
          return false;
      }
  
      if (which_trans == transformation::appendage)
      {
          const mutation_type app = _beastly_appendage();
          if (app == NUM_MUTATIONS)
          {
              msg = "You have no appropriate body parts free.";
              success = false; // XXX: VERY dubious, since an untransform occurred
          }
  
          if (!just_check)
          {
              you.attribute[ATTR_APPENDAGE] = app; // need to set it here so
                                                   // the message correlates
          }
      }
  
      if (!success)
      {
          // Message is not printed if we're updating fail_reason.
          if (fail_reason)
              *fail_reason = msg;
          else if (!involuntary)
              mpr(msg);
          return false;
      }
  
      // If we're just pretending return now.
      if (just_check)
          return true;
  
      // All checks done, transformation will take place now.
      you.redraw_quiver       = true;
      you.redraw_evasion      = true;
      you.redraw_armour_class = true;
      you.wield_change        = true;
      if (form_changed_physiology(which_trans))
          merfolk_stop_swimming();
  
      if (which_trans == transformation::hydra)
          set_hydra_form_heads(div_rand_round(pow, 10));
  
      // Give the transformation message.
      mpr(get_form(which_trans)->transform_message(previous_trans));
  
      // Update your status.
      // Order matters here, take stuff off (and handle attendant HP and stat
      // changes) before adjusting the player to be transformed.
      _remove_equipment(rem_stuff, which_trans);
  
      you.form = which_trans;
      you.set_duration(DUR_TRANSFORMATION, _transform_duration(which_trans, pow));
      update_player_symbol();
  
      you.props[TRANSFORM_POW_KEY] = pow;
  
      const int str_mod = get_form(which_trans)->str_mod;
      const int dex_mod = get_form(which_trans)->dex_mod;
  
      if (str_mod)
          notify_stat_change(STAT_STR, str_mod, true);
  
      if (dex_mod)
          notify_stat_change(STAT_DEX, dex_mod, true);
  
      calc_hp(true, false);
  
      if (you.digging && !form_keeps_mutations(which_trans))
      {
          mpr("Your mandibles meld away.");
          you.digging = false;
      }
  
      // Extra effects
      switch (which_trans)
      {
      case transformation::statue:
          if (you.duration[DUR_ICY_ARMOUR])
          {
              mprf(MSGCH_DURATION, "Your new body cracks your icy armour.");
              you.duration[DUR_ICY_ARMOUR] = 0;
          }
          break;
  
      case transformation::spider:
          leave_web();
          break;
  
      case transformation::tree:
          mpr("Your roots penetrate the ground.");
          if (you.duration[DUR_TELEPORT])
          {
              you.duration[DUR_TELEPORT] = 0;
              mpr("You feel strangely stable.");
          }
          you.duration[DUR_FLIGHT] = 0;
          // break out of webs/nets as well
  
      case transformation::dragon:
          if (you.attribute[ATTR_HELD])
          {
              trap_def *trap = trap_at(you.pos());
              if (trap && trap->type == TRAP_WEB)
              {
                  mpr("You shred the web into pieces!");
                  destroy_trap(you.pos());
              }
              int net = get_trapping_net(you.pos());
              if (net != NON_ITEM)
              {
                  mpr("The net rips apart!");
                  destroy_item(net);
              }
  
              stop_being_held();
          }
          break;
  
      case transformation::lich:
          // undead cannot regenerate -- bwr
          if (you.duration[DUR_REGENERATION])
          {
              mprf(MSGCH_DURATION, "You stop regenerating.");
              you.duration[DUR_REGENERATION] = 0;
          }
  
          you.hunger_state = HS_SATIATED;  // no hunger effects while transformed
          you.redraw_status_lights = true;
          break;
  
      case transformation::appendage:
          {
              int app = you.attribute[ATTR_APPENDAGE];
              ASSERT(app != NUM_MUTATIONS);
              ASSERT(beastly_slot(app) != EQ_NONE);
              you.mutation[app] = _beastly_appendage_level(app);
          }
          break;
  
      case transformation::shadow:
          drain_player(25, true, true);
          if (you.invisible())
              mpr("You fade into the shadows.");
          else
              mpr("You feel less conspicuous.");
          break;
  
      default:
          break;
      }
  
      // Stop constricting if we can no longer constrict. If any size-changing
      // transformations were to allow constriction, we would have to check
      // relative sizes as well. Likewise, if any transformations were to allow
      // normally non-constricting players to constrict, this would need to
      // be changed.
      if (!form_keeps_mutations(which_trans))
          you.stop_directly_constricting_all(false);
  
      // Stop being constricted if we are now too large.
      if (you.is_directly_constricted())
      {
          actor* const constrictor = actor_by_mid(you.constricted_by);
          ASSERT(constrictor);
  
          if (you.body_size(PSIZE_BODY) > constrictor->body_size(PSIZE_BODY))
              you.stop_being_constricted();
      }
  
  
      // If we are no longer living, end an effect that afflicts only the living
      if (you.duration[DUR_FLAYED] && !(you.holiness() & MH_NATURAL))
      {
          // Heal a little extra if we gained max hp from this transformation
          if (form_hp_mod() != 10)
          {
              int dam = you.props["flay_damage"].get_int();
              you.heal((dam * form_hp_mod() / 10) - dam);
          }
          heal_flayed_effect(&you);
      }
  
      // This only has an effect if the transformation happens passively,
      // for example if Xom decides to transform you while you're busy
      // running around or butchering corpses.
      // If you're turned into a tree, you stop taking stairs.
      stop_delay(which_trans == transformation::tree);
  
      if (crawl_state.which_god_acting() == GOD_XOM)
         you.transform_uncancellable = true;
  
      // Land the player if we stopped flying.
      if (was_flying && !you.airborne())
          move_player_to_grid(you.pos(), false);
  
      // Update merfolk swimming for the form change.
      if (you.species == SP_MERFOLK)
          merfolk_check_swimming(false);
  
      if (you.hp <= 0)
      {
          ouch(0, KILLED_BY_FRAILTY, MID_NOBODY,
               make_stringf("gaining the %s transformation",
                            transform_name(which_trans)).c_str());
      }
  
      return true;
  }
  
  /**
   * End the player's transformation and return them to their normal
   * form.
   * @param skip_move      If true, skip any move that was in progress before
   *                       the transformation ended.
   */
  void untransform(bool skip_move)
  {
      const bool was_flying = you.airborne();
  
      you.redraw_quiver           = true;
      you.redraw_evasion          = true;
      you.redraw_armour_class     = true;
      you.wield_change            = true;
      you.received_weapon_warning = false;
      if (you.props.exists(TRANSFORM_POW_KEY))
          you.props.erase(TRANSFORM_POW_KEY);
      if (you.props.exists(HYDRA_FORM_HEADS_KEY))
          you.props.erase(HYDRA_FORM_HEADS_KEY);
  
      // Must be unset first or else infinite loops might result. -- bwr
      const transformation old_form = you.form;
  
      // We may have to unmeld a couple of equipment types.
      set<equipment_type> melded = _init_equipment_removal(old_form);
  
      you.form = transformation::none;
      you.duration[DUR_TRANSFORMATION] = 0;
      update_player_symbol();
  	
      if (old_form == transformation::appendage)
      {
          mutation_type app = static_cast<mutation_type>(you.attribute[ATTR_APPENDAGE]);
          ASSERT(beastly_slot(app) != EQ_NONE);
          const int levels = you.get_base_mutation_level(app);
          // Preserve extra mutation levels acquired after transforming.
          const int beast_levels = _beastly_appendage_level(app);
          const int extra = max(0, levels - you.get_innate_mutation_level(app)
                                          - beast_levels);
          you.mutation[app] = you.get_innate_mutation_level(app) + extra;
          you.attribute[ATTR_APPENDAGE] = 0;
  
          // The mutation might have been removed already by a conflicting
          // demonspawn innate mutation; no message then.
          if (levels)
          {
              const char * const verb = you.has_mutation(app) ? "shrink"
                                                              : "disappear";
              mprf(MSGCH_DURATION, "Your %s %s%s.",
                   mutation_name(app), verb,
                   app == MUT_TENTACLE_SPIKE ? "s" : "");
          }
      }
  
      calc_hp(true, false);
  
      const string message = get_form(old_form)->get_untransform_message();
      if (!message.empty())
          mprf(MSGCH_DURATION, "%s", message.c_str());
  
      const int str_mod = get_form(old_form)->str_mod;
      const int dex_mod = get_form(old_form)->dex_mod;
  
      if (str_mod)
          notify_stat_change(STAT_STR, -str_mod, true);
  
      if (dex_mod)
          notify_stat_change(STAT_DEX, -dex_mod, true);
  
      _unmeld_equipment(melded);
  
      if (!skip_move)
      {
          // Land the player if we stopped flying.
          if (is_feat_dangerous(grd(you.pos())))
              enable_emergency_flight();
          else if (was_flying && !you.airborne())
              move_player_to_grid(you.pos(), false);
  
          // Update merfolk swimming for the form change.
          if (you.species == SP_MERFOLK)
              merfolk_check_swimming(false);
      }
  
  #ifdef USE_TILE
      if (you.species == SP_MERFOLK)
          init_player_doll();
  #endif
  
      // If nagas wear boots while transformed, they fall off again afterwards:
      // I don't believe this is currently possible, and if it is we
      // probably need something better to cover all possibilities.  -bwr
  
      // Removed barding check, no transformed creatures can wear barding
      // anyway.
      // *coughs* Ahem, blade hands... -- jpeg
      if (you.species == SP_NAGA || you.species == SP_CENTAUR)
      {
          const int arm = you.equip[EQ_BOOTS];
  
          if (arm != -1 && you.inv[arm].sub_type == ARM_BOOTS)
              remove_one_equip(EQ_BOOTS);
      }
  
      // End Ozocubu's Icy Armour if you unmelded wearing heavy armour
      if (you.duration[DUR_ICY_ARMOUR]
          && !player_effectively_in_light_armour())
      {
          you.duration[DUR_ICY_ARMOUR] = 0;
  
          const item_def *armour = you.slot_item(EQ_BODY_ARMOUR, false);
          mprf(MSGCH_DURATION, "%s cracks your icy armour.",
               armour->name(DESC_YOUR).c_str());
      }
  
      if (you.hp <= 0)
      {
          ouch(0, KILLED_BY_FRAILTY, MID_NOBODY,
               make_stringf("losing the %s form",
                            transform_name(old_form)).c_str());
      }
  
      // Stop being constricted if we are now too large.
      if (you.is_directly_constricted())
      {
          actor* const constrictor = actor_by_mid(you.constricted_by);
          if (you.body_size(PSIZE_BODY) > constrictor->body_size(PSIZE_BODY))
              you.stop_being_constricted();
      }
  
      you.turn_is_over = true;
      if (you.transform_uncancellable)
          you.transform_uncancellable = false;
  }
  
  void emergency_untransform()
  {
      mpr("You quickly transform back into your natural form.");
      untransform(true); // We're already entering the water.
  
      if (you.species == SP_MERFOLK)
          merfolk_start_swimming(false);
  }
  
  /**
   * Update whether a merfolk should be swimming.
   *
   * Idempotent, so can be called after position/transformation change without
   * redundantly checking conditions.
   *
   * @param stepped Whether the player is performing a normal walking move.
   */
  void merfolk_check_swimming(bool stepped)
  {
      const dungeon_feature_type grid = env.grid(you.pos());
      if (you.ground_level()
          && feat_is_water(grid)
          && !form_changed_physiology(you.form))
      {
          merfolk_start_swimming(stepped);
      }
      else if (!is_feat_dangerous(grid)) // don't bother, the player is dying
          merfolk_stop_swimming();
  }
  
  void merfolk_start_swimming(bool stepped)
  {
      if (you.fishtail)
          return;
  
      if (stepped)
          mpr("Your legs become a tail as you enter the water.");
      else
          mpr("Your legs become a tail as you dive into the water.");
  
      if (you.invisible())
          mpr("...but don't expect to remain undetected.");
  
      you.fishtail = true;
      remove_one_equip(EQ_BOOTS);
      you.redraw_evasion = true;
  
  #ifdef USE_TILE
      init_player_doll();
  #endif
  }
  
  void merfolk_stop_swimming()
  {
      if (!you.fishtail)
          return;
      you.fishtail = false;
      unmeld_one_equip(EQ_BOOTS);
      you.redraw_evasion = true;
  
  #ifdef USE_TILE
      init_player_doll();
  #endif
  }
  